/* ==========================================================================
   ZIKO ART — design tokens
   ========================================================================== */
:root{
  /* 3 base brand colors — everything else is a tint/shade of these */
  --ink:        #191510;
  --paper:      #fbf7ee;
  --accent:     #f2971d;

  /* tints/shades derived from the 3 base colors above */
  --paper-2:    #f1e9d6;
  --ink-soft:   #55503f;
  --ink-faint:  #948c74;
  --line:       #e6dcc2;
  --accent-ink: #2a1400;
  --accent-soft:#fbe2b8;
  --accent-soft-2: #f6cf8c;

  --rail-w: 300px;
  --gutter: clamp(1.5rem, 4vw, 3.4rem);

  --ff-display: "Anton", "Oswald", sans-serif;
  --ff-title: "Oswald", sans-serif;
  --ff-body: "Open Sans", "Segoe UI", sans-serif;
  --ff-mono: "IBM Plex Mono", ui-monospace, monospace;
  --ff-code: "Fira Sans", sans-serif;

  --ease: cubic-bezier(.16,.8,.24,1);
}

*, *::before, *::after{ box-sizing: border-box; }
html{ scroll-behavior: smooth; }
html, body{ background: var(--paper); }
body{
  margin: 0;
  color: var(--ink);
  font-family: var(--ff-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
img{ max-width: 100%; display: block; }
a{ color: inherit; text-decoration: none; }
ul{ list-style: none; margin: 0; padding: 0; }
button{ font: inherit; cursor: pointer; }
::selection{ background: var(--accent); color: #fff; }

h1,h2,h3,h4{
  font-family: var(--ff-title);
  font-weight: 700;
  text-transform: uppercase;
  line-height: 1.08;
  margin: 0;
  letter-spacing: 0.04em;
  text-wrap: balance;
}

.mono{
  font-family: var(--ff-mono);
  font-size: .78rem;
  letter-spacing: .02em;
}
.eyebrow{
  font-family: var(--ff-mono);
  font-size: .78rem;
  color: var(--ink-faint);
  text-transform: uppercase;
  letter-spacing: .1em;
}
.ink-soft{ color: var(--ink-soft); }

/* ==========================================================================
   Shell layout: fixed left rail + scrolling main
   ========================================================================== */
.shell{
  display: grid;
  grid-template-columns: var(--rail-w) 1px 1fr;
  min-height: 100vh;
}
.rail{
  position: sticky;
  top: 0;
  height: 100vh;
  padding: 2.6rem 2rem;
  display: flex;
  flex-direction: column;
  gap: 2.2rem;
}
.rail-spine{ position: relative; overflow: hidden; background: var(--line); }
.rail-spine-fill{ position: absolute; inset: 0; background: var(--accent); transform: scaleY(0); transform-origin: top; }

.rail-id{ display: flex; flex-direction: column; align-items: center; text-align: center; gap: 1rem; }
.rail-id .avatar{
  width: 220px; height: 220px; border-radius: 50%;
  background: transparent;
  display: grid; place-items: center;
  overflow: hidden;
}
.rail-id .avatar img{ width: 100%; height: 100%; object-fit: cover; object-position: top center; }
.rail-id .name{ font-family: var(--ff-title); font-weight: 700; text-transform: uppercase; letter-spacing: .03em; font-size: 1.35rem; }
.rail-id-logo{ height: 30px; width: auto; display: block; margin: 0 auto; }
.rail-id .role{ font-size: .8rem; color: var(--ink-faint); margin-top: .2rem; }

.rail-status{
  display: flex; align-items: center; gap: .55rem;
  font-family: var(--ff-mono); font-size: .72rem; color: var(--ink-soft);
  padding: .5rem .8rem;
  background: var(--paper-2);
  border-radius: 999px;
  width: fit-content;
}
.rail-status .dot{ width: 7px; height: 7px; border-radius: 50%; background: #3fa35c; box-shadow: 0 0 0 3px rgba(63,163,92,.18); }

.rail-nav{ display: flex; flex-direction: column; gap: .2rem; }
.rail-nav a{
  display: flex; align-items: center; gap: .8rem;
  padding: .6rem .5rem;
  border-radius: 10px;
  font-size: .95rem; font-weight: 600;
  color: var(--ink-soft);
  transition: background .25s var(--ease), color .25s var(--ease), padding-left .25s var(--ease);
}
.rail-nav a .ix{ font-family: var(--ff-mono); font-size: .72rem; color: var(--ink-faint); width: 1.4em; }
.rail-nav a:hover{ background: var(--paper-2); color: var(--ink); padding-left: .8rem; }
.rail-nav a.is-active{ background: var(--accent-soft); color: var(--accent-ink); }
.rail-nav a.is-active .ix{ color: var(--accent); }

.rail-spacer{ flex: 1; }

.rail-contact{ display: flex; flex-direction: column; gap: .4rem; font-size: .85rem; }
.rail-contact a{ color: var(--ink-soft); }
.rail-contact a:hover{ color: var(--accent); }

.rail-social{ display: flex; gap: .6rem; }
.rail-social a{
  width: 36px; height: 36px; border-radius: 50%;
  border: 1px solid var(--line);
  display: grid; place-items: center;
  color: var(--ink-soft);
  transition: all .25s var(--ease);
}
.rail-social a:hover{ background: var(--ink); border-color: var(--ink); color: var(--paper); }
.rail-social svg{ width: 15px; height: 15px; }

.rail-toggle{
  display: none;
  position: fixed; top: 1.4rem; right: 1.4rem; z-index: 300;
  width: 48px; height: 48px; border-radius: 50%;
  background: var(--ink); color: var(--paper);
  border: none;
  align-items: center; justify-content: center;
}

/* Mobile-only wordmark next to the rail-toggle hamburger — the rail (with the
   real ZIKO identity) is hidden off-canvas below 980px, so without this the
   fixed top bar is just a lone button with no brand in sight. */
.mobile-logo{
  display: none;
  position: fixed; top: 1.4rem; left: 1.4rem; z-index: 300;
}
.mobile-logo img{ height: 32px; width: auto; display: block; }
/* The open drawer shows its own ZIKO identity block (avatar + name), so the
   small top-left wordmark is redundant once it's open — hide it rather than
   have both visible at once. */
body:has(.rail.is-open) .mobile-logo{ display: none; }

.main{ min-width: 0; padding-bottom: 4rem; }

/* top-right floating CTA — a plain round arrow button at rest that unfurls
   into the full "Let's work together" pill on hover. The label's max-width
   (not the button's own width, which browsers can't tween to/from "auto")
   is what's actually animated; the button just reflows around it since
   nothing here gives it a fixed width of its own. */
.float-cta{
  position: fixed; top: 2.2rem; right: clamp(1.5rem, 4vw, 3.4rem); z-index: 150;
  display: inline-flex; align-items: center;
  white-space: nowrap;
  background: var(--ink); color: var(--paper);
  height: 54px;
  padding: 0 18px;
  border-radius: 999px;
  font-weight: 600; font-size: .92rem;
  transition: background .3s var(--ease), padding .35s var(--ease);
}
.float-cta:hover{ background: var(--accent); color: var(--accent-ink); padding: 0 22px 0 26px; }
.float-cta .label{
  display: inline-block;
  max-width: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-width .4s var(--ease), opacity .25s var(--ease), margin-right .4s var(--ease);
}
.float-cta:hover .label{ max-width: 220px; opacity: 1; margin-right: .6rem; }
.float-cta .arrow{ display: inline-flex; flex: none; transition: transform .3s var(--ease); }
.float-cta:hover .arrow{ transform: translate(3px,-3px); }

/* "Let's work together" slide-in panel */
.work-panel-backdrop{
  position: fixed; inset: 0; background: rgba(25,21,16,.45);
  opacity: 0; visibility: hidden;
  transition: opacity .35s var(--ease), visibility 0s linear .35s;
  z-index: 380;
}
.work-panel-backdrop.is-open{ opacity: 1; visibility: visible; transition: opacity .35s var(--ease); }

.work-panel{
  position: fixed; top: 0; right: 0; height: 100vh; height: 100dvh;
  width: min(70vw, 760px);
  background: var(--paper);
  border-left: 1px solid var(--line);
  padding: clamp(2.4rem, 4vw, 3.6rem);
  overflow-y: auto;
  transform: translateX(100%);
  transition: transform .5s var(--ease);
  z-index: 390;
}
.work-panel.is-open{ transform: translateX(0); }
.work-panel h2{ font-size: clamp(1.9rem, 3.2vw, 2.6rem); margin-top: .6rem; }
.work-panel > p.ink-soft{ margin-top: .9rem; max-width: 46ch; }
.work-panel-close{
  position: absolute; top: clamp(1.6rem,3vw,2.2rem); right: clamp(1.6rem,3vw,2.2rem);
  width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--line);
  background: transparent; color: var(--ink);
  display: grid; place-items: center; font-size: 1.1rem; line-height: 1;
  transition: background .3s var(--ease), color .3s var(--ease), transform .3s var(--ease);
}
.work-panel-close:hover{ background: var(--ink); color: var(--paper); transform: rotate(90deg); }

.work-toggle{ margin: 2rem 0 1.8rem; }
.work-switcher .work-form{ display: none; }
.work-switcher .work-form.is-active{ display: grid; }

body.work-open{ overflow: hidden; }

@media (max-width: 720px){
  .work-panel{ width: 100vw; padding: 5.5rem 1.6rem 3rem; }
}

/* ==========================================================================
   Chapters (numbered narrative blocks)
   ========================================================================== */
.chapter{
  padding: clamp(2rem, 5.5vw, 4.5rem) var(--gutter);
}

.hero-chapter{ padding-top: clamp(3.5rem, 8vw, 6rem); }
/* Homepage-only: let the hero breathe and fill the first screen — scoped to
   .home so the same .hero-chapter markup used as a compact page-title header
   on About/Projects/Blog/Contact/etc. is untouched. */
.home .hero-chapter{
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: clamp(2rem, 6vw, 4rem);
}
.hero-top{
  display: flex;
  flex-direction: column;
  gap: clamp(1.6rem, 3vw, 2.2rem);
}
.hero-top .hero-sub{ margin-top: 0; }
.hero-tags{
  display: flex; flex-wrap: wrap; gap: .6rem;
  list-style: none; margin: 0; padding: 0;
}
.hero-tags li{
  font-family: var(--ff-mono);
  font-size: .76rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-soft);
  padding: .55rem 1.1rem;
  border: 1px solid var(--line);
  border-radius: 999px;
}
.hero-cta-row{ display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; }
.hero-lines{ max-width: 1080px; }
/* Homepage-only: open up the code-style hero headline — wider letter-spacing
   and more room between the 00/01/02 lines. Scoped to .home so the "solid"
   hero-line variant used as a plain left-aligned page title on About/Projects/
   Blog/Contact/etc. is untouched. Centering is mobile-only — see the
   max-width:899px block below; desktop stays left-aligned. */
.hero-line{
  font-family: var(--ff-code);
  font-size: clamp(2.2rem, 5.8vw, 4.1rem);
  font-weight: 500;
  /* text-transform is inherited — pin it so the mixed-case "code line" text stays
     mixed-case now that .hero-lines is an <h1> (uppercase in the base h1-h4 rule). */
  text-transform: none;
  letter-spacing: -0.01em;
  line-height: 1.08;
  padding: .4rem 0;
}
.home .hero-line{ padding: .7rem 0; letter-spacing: 0.04em; }
.hero-line .ix{ font-family: var(--ff-mono); font-size: .45em; color: var(--ink-faint); margin-right: .5em; }
.hero-line .tag{ color: var(--ink-faint); }
/* Manual break points for lines 01/02 — only wanted on the narrow mobile
   layout where these lines wrap unpredictably; desktop has plenty of width
   and wraps fine (or not at all) on its own, so the <br> stays inert there. */
.hero-break{ display: none; }
.hero-line b{ font-weight: 700; color: var(--accent); }
.hero-line.solid{
  font-family: var(--ff-title);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  line-height: 0.94;
  padding: .05rem 0;
}
.hero-line.solid b{ font-weight: 700; }
.js .hero-lines .hero-line{ visibility: hidden; }
.hero-lines .hero-line.is-typing,
.hero-lines .hero-line.is-typed{ visibility: visible; }
.hero-line.is-typing::after{
  content: "";
  display: inline-block;
  width: .5rem; height: .85em;
  margin-left: .1em;
  background: var(--accent);
  vertical-align: -0.1em;
  animation: caret .9s steps(1) infinite;
}
@keyframes caret{ 50%{ opacity: 0; } }

.hero-sub{
  position: relative;
  margin-top: 2.4rem;
  padding-left: 1.4rem;
  max-width: 54ch;
  font-size: clamp(1.05rem, 1.3vw, 1.25rem);
  line-height: 1.75;
  color: var(--ink-soft);
  border-left: 3px solid var(--accent);
}
.hero-scroll-cue{
  display: flex; align-items: center; justify-content: center; gap: .7rem;
  width: 100%;
  font-family: var(--ff-mono); font-size: .78rem; color: var(--ink-faint);
}
.hero-scroll-cue .circ{
  width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid var(--line);
  display: grid; place-items: center;
  animation: bob 2.2s ease-in-out infinite;
}
@keyframes bob{ 0%,100%{ transform: translateY(0); } 50%{ transform: translateY(4px); } }

/* small FAQ-like mini chapters */
.mini-chapters{ display: flex; flex-direction: column; }
.mini-chapter{
  display: grid;
  grid-template-columns: 90px 1fr;
  gap: 1.5rem;
  padding: 1.6rem 0;
  border-top: 1px solid var(--line);
  transition: padding-left .3s var(--ease), border-top-color .3s var(--ease);
}
.mini-chapter:last-child{ padding-bottom: 0; }
/* Same row-hover language as .rail-nav/.bf-nav (nudge right + accent) rather
   than the bordered-card lift used by .proj-tile/.blog-tile/.skill-card —
   this is a divider list, not a boxed card, so a shadow would float oddly. */
.mini-chapter:hover{ padding-left: .6rem; border-top-color: var(--accent); }
.mini-chapter:hover .mc-ix{ color: var(--accent); }
.mini-chapter .mc-ix{ font-family: var(--ff-mono); color: var(--ink-faint); font-size: .85rem; transition: color .3s var(--ease); }
.mini-chapter h3{ font-size: clamp(1.3rem, 2.4vw, 1.7rem); margin-bottom: .6rem; }
.mini-chapter p{ color: var(--ink-soft); max-width: 60ch; }
@media (max-width: 640px){
  .mini-chapter{ grid-template-columns: 1fr; gap: .5rem; }
}

/* ==========================================================================
   Skills grid (home)
   ========================================================================== */
/* Mobile-first: 2 columns so 8 cards read as a tidy 2x4 block instead of one
   long single-column scroll. Desktop steps up to a 4x2 grid (see the
   min-width:981px override below, matching the rail/top-bar breakpoint). */
.skills-grid{
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: .9rem;
}
.skill-card{
  display: flex;
  flex-direction: column;
  gap: .7rem;
  padding: 1.1rem;
  border: 1px solid var(--line);
  border-radius: 16px;
  transition: border-color .3s var(--ease), transform .3s var(--ease), box-shadow .3s var(--ease);
}
.skill-card:hover{
  border-color: var(--accent);
  transform: translateY(-4px);
  box-shadow: 0 14px 30px rgba(25,21,16,.06);
}
.skill-icon{
  width: 38px; height: 38px;
  border-radius: 10px;
  display: grid; place-items: center;
  background: var(--accent-soft);
  color: var(--accent-ink);
  flex: none;
}
.skill-icon svg{ width: 19px; height: 19px; }
.skill-card h3{ font-size: .92rem; line-height: 1.25; }
.skill-card p{ color: var(--ink-soft); font-size: .8rem; }
@media (min-width: 981px){
  .skills-grid{ grid-template-columns: repeat(4, 1fr); gap: 1.2rem; }
  .skill-card{ gap: 1rem; padding: 1.7rem; border-radius: 18px; }
  .skill-icon{ width: 46px; height: 46px; border-radius: 12px; }
  .skill-icon svg{ width: 22px; height: 22px; }
  .skill-card h3{ font-size: 1.05rem; letter-spacing: 0.01em; line-height: 1.08; }
  .skill-card p{ font-size: .88rem; line-height: 1.55; }
}

/* ==========================================================================
   Section heading
   ========================================================================== */
.chapter-head{ margin-bottom: clamp(1.4rem, 3.2vw, 2.2rem); }
.chapter-head h2{ font-size: clamp(2.4rem, 5.4vw, 3.8rem); margin-top: .7rem; }
.chapter-head p{ color: var(--ink-soft); max-width: 56ch; margin-top: .8rem; }

/* ==========================================================================
   Project cards — pastel gradient + device mock
   ========================================================================== */
/* Flex, not grid: with a fixed number of 1fr grid columns, filtering down to a
   couple of tiles (e.g. a category with 1-2 real projects) leaves the unused
   columns as obvious dead space on the right. Flex-basis sizes each tile to
   the same width a 3-up column would be, but justify-content:center lets a
   short/underfull row (a filtered result set, or just the last row) settle in
   the middle instead of stranding a lone tile on the left. */
.proj-grid{
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 1.4rem;
}

.proj-tile{
  border-radius: 22px;
  padding: 1.6rem 1.6rem 1.4rem;
  display: flex;
  flex-direction: column;
  min-height: 340px;
  position: relative;
  overflow: hidden;
  background: var(--paper);
  border: 1px solid var(--line);
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
  will-change: transform;
}
/* The lift/tilt itself is GSAP-driven (see main.js) and writes straight to
   this element's inline transform, so it's untouched here — this just adds
   the same border-color + shadow feedback .blog-tile and .skill-card get on
   hover, so every bordered card on the site reads as one consistent family
   instead of proj-tile being the one that doesn't visibly react. */
.proj-tile:hover{ border-color: var(--accent); box-shadow: 0 14px 30px rgba(25,21,16,.06); }
.proj-grid > .proj-tile{
  flex: 1 1 calc((100% - 2 * 1.4rem) / 3);
  max-width: calc((100% - 2 * 1.4rem) / 3);
}
@media (max-width: 1100px){
  .proj-grid > .proj-tile{ flex-basis: calc((100% - 1.4rem) / 2); max-width: calc((100% - 1.4rem) / 2); }
}
@media (max-width: 680px){
  .proj-grid > .proj-tile{ flex-basis: 100%; max-width: 100%; }
}

/* Titles are stored with spaces stripped (see project-tile.php), so a long one
   is effectively a single unbreakable "word" — without a break rule it just
   overflows past the tile's edge and gets silently clipped by overflow:hidden
   instead of wrapping. overflow-wrap lets the browser break it mid-word when
   there's nowhere else to break, same as a normal long word would. */
.proj-tile .pt-label{ font-family: var(--ff-title); font-size: 1.05rem; font-weight: 700; text-transform: uppercase; letter-spacing: .02em; overflow-wrap: break-word; }
.proj-tile .pt-mock{
  flex: 1;
  margin: 1rem 0;
  border-radius: 12px;
  overflow: hidden;
  background: #0c0c0d;
  box-shadow: 0 20px 40px -20px rgba(20,15,5,.35);
  display: flex;
  align-items: center;
  justify-content: center;
}
.proj-tile .pt-mock img{ width: 100%; height: 128%; object-fit: cover; object-position: top; aspect-ratio: 16/10; }
.proj-tile .pt-foot{
  display: flex; align-items: center; justify-content: space-between;
  font-size: .82rem; color: var(--ink-soft);
}
.proj-tile .pt-foot .go{
  width: 30px; height: 30px; border-radius: 50%;
  background: var(--ink); color: var(--paper);
  display: grid; place-items: center;
  transition: background .3s var(--ease), transform .3s var(--ease);
}
.proj-tile:hover .pt-foot .go{ background: var(--accent); transform: rotate(45deg); }

/* letter tile variant for projects without a photo */
.proj-tile.letter .pt-mock{
  background: linear-gradient(155deg, rgba(0,0,0,.06), rgba(0,0,0,.02));
  color: var(--ink);
  font-family: var(--ff-display);
  font-weight: 400;
}
.proj-tile.letter .pt-mock span{ font-size: 3.2rem; opacity: .35; }
.horiz-end .pt-mock span{ font-size: 1.4rem; opacity: .55; text-transform: uppercase; letter-spacing: .04em; }

/* ==========================================================================
   Horizontal scroll gallery (home)
   ========================================================================== */
.horiz-wrap{
  position: relative;
  overflow: hidden;
  padding: clamp(1.2rem, 3vw, 2rem) 0 clamp(1.5rem, 4vw, 3rem);
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.horiz-track{
  display: flex;
  gap: 1.4rem;
  width: max-content;
  padding-inline: var(--gutter);
}
.horiz-track .proj-tile{
  width: min(80vw, 400px);
  flex: none;
}
@media (max-width: 899px){
  .horiz-wrap{ overflow-x: visible; }
  /* .horiz-track's own base width:max-content lets it grow to fit all tiles —
     fine on desktop where the ancestor .horiz-wrap clips it and GSAP pans it,
     but on mobile that left the track rendering at its full un-clipped content
     width (e.g. 2200px+) with nothing to scroll internally, which just bled
     the whole page out sideways instead of giving a boxed swipeable row.
     Cap the track to the viewport so overflow-x:auto has something to do. */
  .horiz-track{ width: 100%; max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; scroll-snap-type: x proximity; }
  .horiz-track .proj-tile{ scroll-snap-align: start; }
}

/* ==========================================================================
   Stat strip
   ========================================================================== */
.stat-strip{
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.stat-strip .stat{
  padding: 1.5rem clamp(1rem,2vw,2rem);
  border-left: 1px solid var(--line);
}
.stat-strip .stat:first-child{ border-left: none; }
.stat-strip .stat b{
  font-family: var(--ff-display); font-size: clamp(2.2rem, 4.5vw, 3.2rem); font-weight: 400; display: block;
}
.stat-strip .stat b span{ color: var(--accent); }
.stat-strip .stat small{ font-family: var(--ff-mono); font-size: .75rem; color: var(--ink-faint); text-transform: uppercase; letter-spacing: .06em; }
@media (max-width: 700px){
  .stat-strip{ grid-template-columns: 1fr; }
  .stat-strip .stat{ border-left: none; border-top: 1px solid var(--line); }
  .stat-strip .stat:first-child{ border-top: none; }
}

/* ==========================================================================
   Buttons / pills
   ========================================================================== */
.btn{
  display: inline-flex; align-items: center; gap: .6rem;
  padding: .95rem 1.5rem;
  border-radius: 999px;
  background: var(--ink);
  color: var(--paper);
  font-weight: 600; font-size: .92rem;
  border: 1px solid var(--ink);
  transition: background .3s var(--ease), color .3s var(--ease), border-color .3s var(--ease);
}
.btn:hover{ background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.btn--outline{ background: transparent; color: var(--ink); }
.btn--outline:hover{ background: var(--ink); color: var(--paper); }
.btn--sm{ padding: .65rem 1.1rem; font-size: .82rem; }
.btn .arrow{ display: inline-flex; transition: transform .3s var(--ease); }
.btn:hover .arrow{ transform: translate(3px,-3px); }

.filter-row{ display: flex; flex-wrap: wrap; gap: .6rem; margin-bottom: 2.4rem; }
/* Scoped to the real category filter, not `.work-toggle` — that one already sets
   its own margin (2rem 0 1.8rem) for the "Let's work together" panel and would
   otherwise inherit this margin-top too, since both share the .filter-row class. */
.filter-row:not(.work-toggle){ margin-top: 1.6rem; }
.filter-row button{
  padding: .55rem 1.15rem;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: transparent;
  font-size: .85rem; color: var(--ink-soft);
  transition: all .25s var(--ease);
}
.filter-row button:hover{ border-color: var(--accent); color: var(--ink); }
.filter-row button.is-active{ background: var(--ink); border-color: var(--ink); color: var(--paper); }

.back-link{ display: inline-flex; align-items: center; gap: .5rem; text-transform: uppercase; transition: color .2s var(--ease); }
.back-link:hover{ color: var(--accent); }

/* ==========================================================================
   Process list (numbered, mono index)
   ========================================================================== */
.process-list{ counter-reset: proc; }
.process-item{
  display: grid; grid-template-columns: 70px 1fr;
  gap: 1.4rem;
  padding: 1.8rem 0;
  border-top: 1px solid var(--line);
  transition: padding-left .3s var(--ease), border-top-color .3s var(--ease), border-bottom-color .3s var(--ease);
}
.process-item:last-child{ border-bottom: 1px solid var(--line); }
.process-item:hover{ padding-left: .6rem; border-top-color: var(--accent); }
.process-item:last-child:hover{ border-bottom-color: var(--accent); }
.process-item:hover .pi-ix{ color: var(--accent); }
.process-item .pi-ix{ font-family: var(--ff-mono); color: var(--ink-faint); font-size: .85rem; padding-top: .2rem; transition: color .3s var(--ease); }
.process-item h3{ font-size: 1.25rem; margin-bottom: .4rem; }
.process-item p{ color: var(--ink-soft); max-width: 60ch; font-size: .95rem; }

/* ==========================================================================
   Case study (single project page)
   ========================================================================== */
.case-meta{
  display: flex; flex-wrap: wrap;
  gap: clamp(1.4rem, 3vw, 3rem);
  margin-top: 2rem; padding-top: 1.6rem;
  border-top: 1px solid var(--line);
}
.cm-item{ display: flex; flex-direction: column; gap: .3rem; min-width: 110px; }
.cm-label{ font-family: var(--ff-mono); font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-faint); }
.cm-value{ font-weight: 600; font-size: 1rem; }
.cm-value a{ color: var(--ink); }
.cm-value a:hover{ color: var(--accent); }

.case-cover{ border-radius: 22px; overflow: hidden; box-shadow: 0 30px 60px -30px rgba(20,15,5,.35); }
.case-cover img{ width: 100%; height: auto; max-height: 640px; object-fit: cover; display: block; }

/* ==========================================================================
   Blog cards
   ========================================================================== */
.blog-grid{ display: grid; grid-template-columns: repeat(3,1fr); gap: 1.4rem; }
@media (max-width: 900px){ .blog-grid{ grid-template-columns: 1fr; } }
.blog-tile{
  border: 1px solid var(--line);
  border-radius: 18px;
  padding: 1.6rem;
  background: var(--paper-2);
  transition: transform .3s var(--ease), border-color .3s var(--ease), box-shadow .3s var(--ease);
}
.blog-tile:hover{ transform: translateY(-4px); border-color: var(--accent); box-shadow: 0 14px 30px rgba(25,21,16,.06); }
.blog-tile .bt-tag{
  font-family: var(--ff-mono); font-size: .72rem; color: var(--accent-ink);
  background: var(--accent-soft);
  padding: .3rem .7rem; border-radius: 999px; display: inline-block; margin-bottom: 1rem;
}
.blog-tile h3{ font-size: 1.2rem; margin-bottom: .6rem; }
.blog-tile p{ color: var(--ink-soft); font-size: .9rem; }
.blog-tile .bt-date{ font-family: var(--ff-mono); font-size: .74rem; color: var(--ink-faint); margin-top: 1.2rem; display: block; }

/* ==========================================================================
   Contact
   ========================================================================== */
.contact-grid{ display: grid; grid-template-columns: 1fr 1.2fr; gap: clamp(2rem,5vw,4rem); }
@media (max-width: 860px){ .contact-grid{ grid-template-columns: 1fr; } }
.contact-list{ display: flex; flex-direction: column; }
.contact-list .ci-row{
  display: flex; justify-content: space-between; align-items: baseline;
  padding: 1.3rem 0; border-top: 1px solid var(--line);
}
.contact-list .ci-row:last-child{ border-bottom: 1px solid var(--line); }
.contact-list .ci-row .ci-label{ font-family: var(--ff-mono); font-size: .78rem; color: var(--ink-faint); text-transform: uppercase; }
.contact-list .ci-row a, .contact-list .ci-row span.val{ font-size: 1rem; font-weight: 500; }
.contact-list .ci-row a:hover{ color: var(--accent); }

.form-grid{ display: grid; grid-template-columns: 1fr 1fr; gap: 1.1rem; }
.form-grid .full{ grid-column: 1/-1; }
@media (max-width: 600px){ .form-grid{ grid-template-columns: 1fr; } }
.field label{ display: block; font-family: var(--ff-mono); font-size: .74rem; color: var(--ink-faint); text-transform: uppercase; margin-bottom: .5rem; }
.field input, .field textarea, .field select{
  width: 100%;
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: .9rem 1rem;
  color: var(--ink);
  font-family: inherit;
  font-size: .95rem;
  transition: border-color .3s var(--ease);
}
.field input:focus, .field textarea:focus, .field select:focus{ outline: none; border-color: var(--accent); }
.field textarea{ min-height: 130px; resize: vertical; }

/* ==========================================================================
   Footer — giant wordmark
   ========================================================================== */
.big-footer{
  position: relative;
  padding: clamp(3rem, 8vw, 6rem) var(--gutter) 3rem;
  background: var(--ink);
  color: var(--paper);
  overflow: hidden;
}
.big-footer::before{
  content: "";
  position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: linear-gradient(90deg, var(--accent), transparent 65%);
}
.big-footer::after{
  content: "";
  position: absolute; right: -6vw; bottom: -12vw;
  width: 34vw; height: 34vw; max-width: 460px; max-height: 460px;
  background: radial-gradient(circle, rgba(242,151,29,.14), transparent 70%);
  pointer-events: none;
}

.bf-grid{
  position: relative;
  display: grid;
  grid-template-columns: 1.3fr .9fr .9fr 1fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}

.bf-cta-text{ color: rgba(251,248,242,.65); font-size: .92rem; line-height: 1.6; margin-bottom: 1.4rem; }
/* .btn defaults to a dark pill (ink bg / paper text), which disappears against
   this footer's own dark background — swap it for the accent color instead. */
.bf-cta-btn{ background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.bf-cta-btn:hover{ background: var(--accent-soft); border-color: var(--accent-soft); }

.bf-brand .bf-logo{ margin-bottom: 1.4rem; }
.bf-brand .bf-logo img{ height: clamp(40px, 6vw, 64px); width: auto; }
.bf-tagline{ color: rgba(251,248,242,.65); max-width: 34ch; font-size: .98rem; line-height: 1.6; margin-bottom: 1.8rem; }

.bf-social{ display: flex; gap: .6rem; }
.bf-social a{
  width: 34px; height: 34px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid rgba(251,248,242,.2); color: rgba(251,248,242,.7);
  transition: background .2s ease, border-color .2s ease, color .2s ease;
}
.bf-social a:hover{ background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.bf-social svg{ width: 14px; height: 14px; }

.bf-eyebrow{
  font-family: var(--ff-mono); font-size: .74rem; text-transform: uppercase; letter-spacing: .1em;
  color: rgba(251,248,242,.4); display: block; margin-bottom: 1.3rem;
}
.bf-nav{ display: flex; flex-direction: column; }
.bf-nav a, .bf-nav .bf-static{
  display: flex; align-items: baseline; gap: .7rem;
  padding: .55rem 0; border-bottom: 1px solid rgba(251,248,242,.08);
  color: rgba(251,248,242,.85); font-size: .98rem;
}
.bf-nav a .ix{ font-family: var(--ff-mono); font-size: .7rem; color: rgba(251,248,242,.35); }
.bf-nav a{ transition: color .2s ease, padding-left .2s ease; }
.bf-nav a:hover{ color: var(--accent); padding-left: .5rem; }
.bf-nav .bf-static{ color: rgba(251,248,242,.65); }

.big-footer .bf-bottom{
  position: relative;
  margin-top: 3.5rem; padding-top: 1.6rem; border-top: 1px solid rgba(251,248,242,.15);
  display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 1rem;
  font-family: var(--ff-mono); font-size: .74rem; color: rgba(251,248,242,.45);
}
.bf-top{ color: rgba(251,248,242,.45); transition: color .2s ease; }
.bf-top:hover{ color: var(--accent); }

.bf-legal{ display: flex; flex-wrap: wrap; gap: 1.4rem; }
.bf-legal a{ color: rgba(251,248,242,.45); transition: color .2s ease; }
.bf-legal a:hover{ color: var(--accent); }

@media (max-width: 760px){
  .bf-grid{ grid-template-columns: 1fr; gap: 2.4rem; }
}

/* ==========================================================================
   Reveal
   ========================================================================== */
/* .reveal elements are visible by default; main.js's IntersectionObserver (not GSAP —
   see the note in main.js on why this moved off ScrollTrigger) adds .is-revealing
   pre-scroll and .is-revealed once each element enters view, and only then do the
   hidden/transition styles below apply. A failed/blocked script never leaves content
   permanently invisible — CSS alone always renders it visible. */
.is-revealing.reveal{
  opacity: 0;
  transform: translateY(28px);
  transition: opacity .7s ease, transform .7s ease;
}
.is-revealing.reveal.is-revealed{ opacity: 1; transform: none; }
.is-revealing.proj-tile.reveal{ transform: translateY(14px); transition-duration: .4s; }
.proj-grid .proj-tile:nth-child(3n+2){ transition-delay: .05s; }
.proj-grid .proj-tile:nth-child(3n+3){ transition-delay: .1s; }

/* Responsive rail -> top bar */
@media (max-width: 980px){
  .shell{ grid-template-columns: 1fr; }
  .rail-spine{ display: none; }
  .rail{
    position: fixed; inset: 0 0 0 auto; width: min(320px, 84vw);
    z-index: 250;
    background: var(--paper);
    box-shadow: -30px 0 60px rgba(0,0,0,.12);
    transform: translateX(100%);
    transition: transform .4s var(--ease);
    height: 100dvh;
  }
  .rail.is-open{ transform: translateX(0); }
  /* At the desktop avatar size (220px) plus 5 nav links, contact and social
     rows, the drawer's content is taller than most phone viewports — with no
     scroll on this fixed-position panel, the bottom items (contact/social)
     were pushed off-screen and unreachable. Shrink the avatar/identity block
     and tighten the rail's own spacing so everything fits; overflow-y is a
     safety net for shorter screens rather than the primary fix. */
  .rail{ padding: 2rem 1.6rem; gap: 1.3rem; overflow-y: auto; }
  .rail-id{ flex-direction: row; text-align: left; gap: .8rem; }
  .rail-id .avatar{ width: 56px; height: 56px; flex: none; }
  .rail-id-logo{ margin: 0; }
  /* The logo + hamburger button are two independent fixed elements with no
     bar behind them, so scrolled content showed straight through the gaps
     around them. Paint a solid strip matching the page background so the
     top bar reads as opaque instead of transparent. */
  body::before{
    content: "";
    position: fixed;
    top: 0; left: 0; right: 0;
    height: 5rem;
    background: var(--paper);
    z-index: 200;
    pointer-events: none;
  }
  .rail-toggle{ display: flex; }
  .mobile-logo{ display: flex; }
  .float-cta{ display: none; }
  .main{ padding-top: 5rem; }
  /* .main's padding-top above already clears the fixed top bar, so
     .hero-chapter's own desktop-sized top padding (meant to balance the tall
     empty rail sidebar, which doesn't exist here) just stacks on top of it —
     shrink it down to a normal chapter gap instead of ~2 fixed-bar-heights of
     empty space before any content shows. */
  .hero-chapter{ padding-top: clamp(1.5rem, 6vw, 2.5rem); }
  /* Desktop forces the hero to exactly 100vh and splits the leftover space
     between the text block and the scroll cue with justify-content:space-between
     — that only looks right when the content height happens to land close to
     the viewport height. Phone screens vary too much in height/width for that
     match to hold: on a taller phone (or once the text wraps a line less than
     expected) the leftover space piles up as one big empty gap in the middle.
     So on mobile we drop min-height and space-between entirely and let the
     hero size itself to its content, with a normal fixed gap before the
     scroll cue — same type size, just no more fragile full-screen stretch. */
  .home .hero-chapter{ min-height: 0; justify-content: flex-start; gap: 2rem; }
  .home .hero-top{ gap: 1rem; }
  /* Centering this multi-line "00 <tag>text<tag>" code-style headline made it
     look cramped/off-balance on phones — the leading index number sits outside
     the centered sentence, so each line's optical center shifts, and short
     wrapped lines left big empty margins on both sides instead of reading as
     a full-width headline. Left-aligned (same as desktop) uses the full
     available width edge-to-edge instead. */
  .home .hero-lines{ text-align: left; }
  .home .hero-line{ font-size: 1.7rem; }
  .home .hero-break{ display: block; }
  .home .hero-tags{ gap: .5rem; }
  .home .hero-tags li{ padding: .45rem .9rem; font-size: .7rem; }
  .home .hero-cta-row{ gap: .7rem; }
}

/* ==========================================================================
   WordPress integration additions (not in the original static mockup)
   ========================================================================== */

/* Contact Form 7: forms are built with the same .form-grid/.field markup as the
   mockup's demo forms, so inputs/labels/buttons already match. This covers CF7's
   own wrapper + validation/response elements only. */
.wpcf7-form .form-grid{ position: relative; }
.wpcf7-not-valid-tip{
  display: block;
  font-size: .78rem;
  color: #c23b22;
  margin-top: .4rem;
}
.wpcf7-response-output{
  margin: 1.2rem 0 0;
  padding: .8rem 1rem;
  border-radius: 10px;
  border: 1px solid var(--line);
  font-size: .88rem;
}
.wpcf7-form.sent .wpcf7-response-output{ border-color: var(--accent); color: var(--accent-ink); background: var(--accent-soft); }
.wpcf7-form.invalid .wpcf7-response-output,
.wpcf7-form.unaccepted .wpcf7-response-output,
.wpcf7-form.payment-required .wpcf7-response-output{ border-color: #c23b22; color: #c23b22; }
.wpcf7-spinner{ margin-left: .6rem; }
.screen-reader-response{ position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(1px,1px,1px,1px); }

/* Blog/pagination links reuse the pill-button look of .filter-row, but paginate_links()
   and the_posts_pagination() render <a>/<span>, not <button>. */
.filter-row a,
.filter-row .page-numbers{
  display: inline-flex; align-items: center;
  padding: .55rem 1.15rem;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: transparent;
  font-size: .85rem; color: var(--ink-soft);
  transition: all .25s var(--ease);
}
.filter-row a:hover{ border-color: var(--accent); color: var(--ink); }
.filter-row .page-numbers.current{ background: var(--ink); border-color: var(--ink); color: var(--paper); }
.pagination, .nav-links{ display: flex; flex-wrap: wrap; gap: .6rem; }

/* Real project/post content (the_content()) — Elementor-built project pages and blog
   posts aren't hand-designed per-page like the mockup, so give the generic markup
   sensible typography inside the chapter system instead of inheriting the all-caps
   .chapter h2/h3 treatment meant for section headings. */
.post-content{ max-width: 74ch; }
.project-content{ max-width: none; }
.project-content p, .project-content ul, .project-content ol{ max-width: 74ch; }
.project-content :where(h1,h2,h3),
.post-content :where(h1,h2,h3){
  font-size: clamp(1.6rem, 3vw, 2.1rem);
}
.project-content h4,
.post-content h4{
  font-size: clamp(1.25rem, 2vw, 1.5rem);
}
/* Same sizes again, but specific enough (tag + .elementor-heading-title, both
   !important) to beat the "font-size: inherit !important" rule below — that
   rule resets Elementor's own per-widget font-size, which otherwise wins over
   the plain tag rules above since inherit pulls from the parent container
   (16px), not from these sibling tag selectors. */
.project-content h1.elementor-heading-title,
.project-content h2.elementor-heading-title,
.project-content h3.elementor-heading-title,
.post-content h1.elementor-heading-title,
.post-content h2.elementor-heading-title,
.post-content h3.elementor-heading-title{
  font-size: clamp(1.6rem, 3vw, 2.1rem) !important;
}
.project-content h4.elementor-heading-title,
.post-content h4.elementor-heading-title{
  font-size: clamp(1.25rem, 2vw, 1.5rem) !important;
}
.project-content :where(h1,h2,h3,h4),
.post-content :where(h1,h2,h3,h4){
  text-transform: none;
  font-family: var(--ff-title);
  margin: 2rem 0 .8rem;
}
.project-content :where(h5,h6),
.post-content :where(h5,h6){
  font-family: var(--ff-mono);
  font-size: .78rem;
  color: var(--ink-faint);
  text-transform: uppercase;
  letter-spacing: .1em;
  margin: 2rem 0 .8rem;
}
.project-content p, .post-content p{ margin: 0 0 1.2rem; color: var(--ink-soft); }
.project-content ul, .post-content ul{ list-style: disc; margin: 0 0 1.2rem 1.4rem; color: var(--ink-soft); }
.project-content ol, .post-content ol{ margin: 0 0 1.2rem 1.4rem; color: var(--ink-soft); }
.project-content img, .post-content img{ border-radius: 14px; margin: 1.6rem 0; }
.project-content a, .post-content a{ color: var(--accent); text-decoration: underline; }

/* ==========================================================================
   Legacy Onovo case-study content (the_content() on single-portfolio.php)
   ========================================================================== */
/* Every one of the 37 published projects was built by duplicating the same
   Onovo-era Elementor template (confirmed identical element IDs across a sample
   of the catalog) — only the text/images/colors were swapped per project. Rather
   than patch that template's own widgets to look new, kill Elementor's box model
   outright inside .project-content: no more nested sections/columns/dark cards,
   just the real per-project text and photos, laid out with this theme's own
   typography like any other page. Real content (description copy, the "Project
   Requirement" tags, the photo gallery) is kept and restyled; everything that was
   pure old-template chrome (duplicate title, duplicate mockup image, the black
   Client/Start/Website/Share card, the old prev/next widget) is removed, because
   the new hero + meta grid + "Related projects" section already cover that ground. */

/* 1. Flatten every Elementor/e-con wrapper: no width caps, no flex/grid column
      splits, no backgrounds/borders/shadows/padding — just block-stacked content
      so .project-content's own typography rules (below) do all the styling. */
.project-content .elementor-section,
.project-content .elementor-container,
.project-content .elementor-column,
.project-content .elementor-widget-wrap,
.project-content .elementor-inner-section,
.project-content .e-con,
.project-content .e-con-inner{
  display: block !important;
  width: 100% !important;
  max-width: 100% !important;
  background: none !important;
  border: none !important;
  box-shadow: none !important;
  padding: 0 !important;
  margin: 0 !important;
}
.project-content .elementor-widget-container{ margin: 0 0 .2rem !important; }
/* Elementor writes an explicit font-size/weight into each post's own generated CSS
   for every heading widget — drop those so the tag-based h1-h6 rules below (which
   already set the right font-family/case/color for this design) are what wins. */
.project-content .elementor-heading-title{
  font-size: inherit !important; font-weight: inherit !important;
}

/* 2. Old-template chrome that's pure duplication of what the new template already
   shows elsewhere on the page — gone, not reskinned. */
.project-content .onovo-intro,                                  /* duplicate title, redundant with the hero */
.project-content .onovo-share,                                  /* dropping the share feature entirely */
.project-content .elementor-inner-section[data-settings*="background_background"], /* the black Client/Start/Website/Share card — Client≈title, Start≈our Date field, Website≈our "Live site" link, all shown already */
.project-content .e-con:has(.elementor-widget-onovo-project-nav-links) /* old prev/next, replaced by "Related projects" */
{ display: none !important; }

/* 1c. The mockup image (elementor-element-4f24427 desktop / 0ca93bf mobile — Elementor's
   own responsive classes already show exactly one of the two per viewport) used to be
   hidden as a duplicate of the hero cover image. Instead of hiding it, pair it with the
   "Description" section right after it as a 50/50 row — this mirrors the original
   template's own two-column layout (Description | card), just with the real screenshot
   swapped in for the card we removed. Not every one of the 37 projects has this image
   widget, so the Description section only goes half-width when it's actually preceded
   by it (adjacent-sibling selector) — otherwise it stays full width like any other
   section, instead of leaving a blank second column. */
.project-content > .elementor{
  display: grid; grid-template-columns: 1fr; gap: clamp(1.5rem, 4vw, 2.5rem);
}
.project-content > .elementor > *{ grid-column: 1 / -1; }
.project-content .elementor-element-4f24427 img,
.project-content .elementor-element-0ca93bf img{
  border-radius: 18px; width: 100%; height: 100%; object-fit: cover; margin: 0 !important;
}
.project-content .elementor-element-7d35bc8 p,
.project-content .elementor-element-7d35bc8 ul,
.project-content .elementor-element-7d35bc8 ol{
  max-width: none;
}
/* This text-editor widget carries its own inline width (Elementor's per-widget
   "_element_width" control, used to sit it beside another widget) — our
   flatten rules above reset section/column/e-con wrappers but not the widget
   element itself, so it was still rendering at half width and making
   Description look like a two-column split. */
.project-content .elementor-element-a76cf9c{
  width: 100% !important;
  max-width: 100% !important;
  flex: 0 0 100% !important;
}

/* 3. What's left — a "Description" heading + paragraphs, a "Project Requirement"
   heading + icon-list, and an image gallery — is real per-project content. Style
   the icon-list as small tag pills instead of Elementor's default checklist. */
.project-content .elementor-icon-list-items{
  display: flex; flex-wrap: wrap; gap: .5rem; margin: 0 0 1.2rem; list-style: none; padding: 0;
}
.project-content .elementor-icon-list-item{
  display: inline-flex; align-items: center;
  padding: .4rem .9rem; border: 1px solid var(--line); border-radius: 999px;
  font-family: var(--ff-mono); font-size: .74rem; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-soft);
}
.project-content .elementor-icon-list-icon{ display: none !important; }

/* 4. Image gallery — plain responsive grid instead of WP's inline gallery styles. */
.project-content .gallery{
  display: grid !important; grid-template-columns: repeat(2, 1fr); gap: 1rem;
  margin: 1.6rem 0 !important; padding: 0 !important;
}
/* WP's own classic-gallery stylesheet ships a `.gallery-columns-N .gallery-item`
   rule (max-width: 100%/N, e.g. 50% for the gallery-columns-2 class WP put on
   this markup) sized for whatever column count the gallery was originally
   configured with. Our grid-template-columns above overrides the container, but
   that WP rule still caps each item's own width — silently shrinking every photo
   to half its grid cell regardless of how many columns we actually render.
   Reset it so our grid, not WP's leftover class, decides each item's size. */
.project-content .gallery-item{ margin: 0 !important; padding: 0 !important; max-width: none !important; }
.project-content .gallery-item img{
  margin: 0; width: 100%; height: 100%; object-fit: cover; object-position: top center;
  aspect-ratio: 4/3; border-radius: 12px;
}
@media (max-width: 640px){ .project-content .gallery{ grid-template-columns: 1fr; } }

/* When the real gallery has been swapped in for the single mockup image (see
   zikoart_pair_gallery_with_description() in functions.php), it becomes the sole
   content of the paired left column (65% of the row, see the grid-template-
   columns above) — drop its default top margin so it starts flush with
   "Description" on the right. 3 columns instead of the default 2: at 65% width
   that's still a comfortable size per photo, and a uniform crop (inherited from
   the base .gallery rule above) keeps the grid tidy even when the source photos
   are wildly different shapes (wide wallpapers, a square logo card, etc). */
.project-content .elementor-element-c88c2cb .gallery{
  grid-template-columns: repeat(3, 1fr); margin-top: 0 !important;
}
@media (max-width: 900px){
  .project-content .elementor-element-c88c2cb .gallery{ grid-template-columns: repeat(2, 1fr); }
}
