/* ── Core Epoch — Shared styles (coreepoch.dev) ── */

/* Self-hosted fonts (latin subsets) — no third-party requests on page load */
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 300 600; font-display: swap;
  src: url('/fonts/inter-latin-300_400_600.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: 'JetBrains Mono'; font-style: normal; font-weight: 400 600; font-display: swap;
  src: url('/fonts/jetbrains-mono-latin-400_600.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;
}

:root {
  --bg: #050505;
  --copper: #d4a574;
  --glass: rgba(10, 10, 12, 0.25);
  --glass-border: rgba(255, 255, 255, 0.06);
  --text: #e8ecf2;
  --text-muted: #8b95a5;
  --sans: 'Inter', -apple-system, sans-serif;
  --mono: 'JetBrains Mono', monospace;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --max-w: 1060px;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body { 
  font-family: var(--sans); 
  background: var(--bg); 
  color: var(--text); 
  line-height: 1.6; 
  overflow-x: hidden; 
  -webkit-font-smoothing: antialiased;
}

/* ── BACKGROUND ── */
.fixed-bg {
  position: fixed;
  top: 0; left: 0;
  width: 100vw; height: 100vh;
  background: url('hero-bg.png') center/cover no-repeat #000;
  background-image: image-set(url('hero-bg.webp') type('image/webp'), url('hero-bg.png') type('image/png'));
  z-index: -1;
  opacity: 0.8;
}
/* The #000 above is load-bearing wherever the art does not cover the whole layer. This
   layer is 0.8 opaque, so an uncovered area shows --bg (#050505) at luminance ~4.4 while
   the art's own black corners composite to ~1-2.5 — a visible vertical step at the
   letterbox edge. Painting the layer black makes both sides composite identically. */
.fixed-bg::after {
  content: ''; 
  position: absolute; inset: 0;
  background: radial-gradient(circle at center, transparent 0%, var(--bg) 110%);
}
.container { max-width: var(--max-w); margin: 0 auto; padding: 0 2rem; }

/* ── NAV ── */
#nav { padding: 2rem 0; position: absolute; width: 100%; z-index: 10; top: 0; left: 0; }
.nav-inner { 
  max-width: var(--max-w); margin: 0 auto; padding: 0 2rem; 
  display: flex; justify-content: space-between; align-items: center; 
}
.nav-logo { 
  font-family: var(--mono); color: var(--text); 
  text-decoration: none; letter-spacing: 0.2em; 
  font-size: 0.9rem; font-weight: 600; 
}
.nav-links { display: flex; gap: 1.5rem; align-items: center; }
.nav-link {
  font-family: var(--sans); color: var(--copper);
  text-decoration: none; font-size: 0.85rem;
  transition: opacity 0.3s var(--ease);
}
.nav-link:hover { opacity: 0.7; }
.nav-cta { 
  font-family: var(--mono); color: var(--copper); 
  text-decoration: none; font-size: 0.8rem; 
  border: 1px solid var(--glass-border); 
  padding: 0.5rem 1rem; border-radius: 4px; 
  transition: border-color 0.3s, background 0.3s; 
  background: var(--glass); backdrop-filter: blur(8px);
}
.nav-cta:hover { border-color: var(--copper); background: rgba(212, 165, 116, 0.1); }

/* ── HERO ── */
#hero { height: 100vh; display: flex; align-items: center; padding: 0 2rem; position: relative; }
.hero-content { max-width: var(--max-w); margin: 0 auto; width: 100%; }
h1 { font-size: clamp(2.5rem, 6vw, 4.5rem); font-weight: 300; letter-spacing: -0.03em; margin-bottom: 1rem; }
#hero h1 { font-weight: 400; line-height: 1.05; max-width: 20ch; text-wrap: balance; }
.hero-line {
  font-family: var(--mono); color: var(--copper);
  font-size: 1rem; letter-spacing: 0.02em; line-height: 1.75;
  max-width: 56ch; margin-top: 1.5rem; text-wrap: pretty;
}
/* Sub-line plate. Replaces the page-wide directional scrim removed above: the copper
   sub-line was the only element that failed over lit art (worst local contrast 1.92:1
   against a strut, vs 4.5:1 needed at this size), while the white h1 reads fine on the
   dark limb. Protecting the one line beats dimming half the image, and matches how
   cryphex.dev already handles its own sub-line. */
#hero .hero-line {
  display: inline-block;
  padding: 0.6rem 0.9rem;
  border-radius: 4px;
  background: rgba(5, 8, 12, 0.62);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--glass-border);
}
.hero-sub { font-family: var(--mono); color: var(--copper); letter-spacing: 0.15em; font-size: 0.85rem; text-transform: uppercase; }

/* ── PRODUCT CARDS ── */
#products { 
  padding: 10vh 0 15vh; 
  background: linear-gradient(to bottom, transparent, rgba(5,5,5,0.8) 40%, var(--bg) 100%); 
  min-height: 100vh; 
}
.products-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; }
.product-card { 
  padding: 2.5rem 2rem; 
  background: var(--glass); 
  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--glass-border); 
  border-radius: 12px; 
  transition: transform 0.4s var(--ease), border-color 0.4s; 
}
.product-card:hover { transform: translateY(-8px); border-color: rgba(212, 165, 116, 0.3); }
.product-mono { font-family: var(--mono); color: var(--text-muted); font-size: 0.7rem; letter-spacing: 0.12em; margin-bottom: 1.5rem; display: block; text-transform: uppercase; }
.product-card h3 { font-size: 1.75rem; font-weight: 400; margin-bottom: 1rem; color: var(--text); letter-spacing: -0.02em; }
/* Cryphex has its own site; the card title is the only route to it from here. The
   heading keeps its own weight and colour -- an underlined, recoloured title fought
   the card's typography and read as a raw browser link. The affordance is the copper
   arrow plus a colour shift on hover, which is the same restraint .product-link uses.
   `color: inherit` matters for more than taste: if this rule ever fails to load, the
   title still renders as a heading rather than as default browser blue. */
.product-title-link {
  color: inherit; text-decoration: none;
  transition: color .3s var(--ease);
}
.product-title-link:hover { color: var(--copper); }
.product-title-link:hover .ext { opacity: 1; transform: translate(2px, -2px); }
.product-title-link:focus-visible { outline: 2px solid var(--copper); outline-offset: 4px; border-radius: 2px; }
.product-title-link .ext {
  display: inline-block;
  font-size: 0.55em; vertical-align: 0.42em;
  color: var(--copper); opacity: 0.8;
  transition: opacity .3s var(--ease), transform .3s var(--ease);
}
.product-card p { font-size: 0.95rem; color: var(--text-muted); line-height: 1.7; }
.product-link {
  font-family: var(--mono); color: var(--copper); 
  text-decoration: none; font-size: 0.8rem; letter-spacing: 0.05em; 
  transition: opacity 0.3s;
}
.product-link:hover { opacity: 0.7; }

/* ── LEGAL PAGES (privacy, terms, commercial-license) ── */
.legal-bg .fixed-bg { opacity: 0.4; }
.legal-bg .fixed-bg::after { background: radial-gradient(circle at center, rgba(5,5,5,0.6) 0%, var(--bg) 110%); }

/* ── HERO-PAGE BACKGROUND (homepage only, same scoping pattern as .legal-bg) ──
   hero-bg.png is square (1440x1440) and the core occupies 88.1% of that frame; the rest
   is padding baked into the art. `cover` on a 16:9 viewport therefore discards ~43% of
   the image height and cuts the top and bottom off the core. `contain` shows all of it
   but lands the core at only 88% of the viewport height. Scaling to 109% of height eats
   the padding instead, putting the core at ~96% with nothing of it cropped. The no-crop
   ceiling is 110%, not 100/88.1 = 113%: that form assumes the padding is symmetric and it
   is not — the core sits high in the frame (top padding 4.7%, bottom 7.4%), so the TOP
   edge clips first. The real limit is 100/(2*(0.5-topPadding)). Verified against rendered
   pixels; at 109% the core's top edge lands ~5px inside the viewport.

   This 109% rule is LANDSCAPE ONLY; portrait is handled by the block below. */
@media (min-aspect-ratio: 1/1) {
  .hero-page .fixed-bg { background-size: auto 109%; }
}
/* Portrait: stack instead of overlay, mirroring cryphex.dev. `cover` on a tall screen
   shows only a middle slice of the square, so the core reads as texture; once it is
   whole it is an OBJECT, and text sitting on an object reads as a collision.

   Sized on HEIGHT, not width: the resource that runs out on a portrait screen is
   height, and a width-based rule put the text on top of the core on wide-but-short
   viewports. `auto 55%` gives the image box the top 55% of the viewport at every
   portrait size. This core is 88.1% of its frame with 4.7% top padding (cryphex's is
   78.2% / 9.9%), so it lands slightly larger here -- ~48% of viewport height against
   ~43% there -- which is why the two sites carry different numbers rather than one
   shared rule.

   The min() is the width guard, and this core needs it where cryphex's does not.
   Sizing on height alone ignores the other constraint: the core must also fit the
   viewport WIDTH. Being 88.1% of its frame, this core reaches 108% of viewport width
   on a 412x915 phone at 55vh and gets clipped left and right. It fits while
   0.881 * H <= vw, i.e. H <= 113vw, so the height cap and the width cap are taken
   together and the smaller wins. cryphex's slimmer core clears the same phone
   unaided but carries the guard too, for tall screens.

   No scrim to switch off here: the directional fade was removed site-wide when the
   sub-line plate replaced it, so the art is already unmodified and the plate keeps
   the copper sub-line legible wherever it lands. */
@media (max-aspect-ratio: 4/5) {
  .hero-page .fixed-bg {
    background-size: auto min(55vh, 113vw);
    background-position: center top;
  }
}
#nav.solid { border-bottom: 1px solid var(--glass-border); background: rgba(5,5,5,0.8); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }
#content { padding: 150px 0 100px; min-height: 100vh; }
.article-container { max-width: 760px; margin: 0 auto; padding: 0 2rem; }
.article-header { margin-bottom: 3rem; text-align: center; }
.article-header h1 { font-size: clamp(1.8rem, 3vw, 2.5rem); font-weight: 300; letter-spacing: -0.02em; line-height: 1.2; margin-bottom: 1rem; }
.article-meta { font-family: var(--mono); color: var(--copper); font-size: 0.8rem; letter-spacing: 0.05em; text-transform: uppercase; }
.paper { 
  background: var(--glass);
  border: 1px solid var(--glass-border);
  border-radius: 12px;
  padding: 3rem 4rem;
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  box-shadow: 0 20px 40px rgba(0,0,0,0.5);
}
.paper h2 { font-size: 1.25rem; font-weight: 600; color: var(--copper); margin: 2rem 0 0.75rem; }
.paper h2:first-child { margin-top: 0; }
.paper p { margin-bottom: 1rem; color: var(--text-muted); font-size: 0.95rem; line-height: 1.8; }
.paper ul, .paper ol { margin-bottom: 1rem; padding-left: 1.5rem; color: var(--text-muted); font-size: 0.95rem; line-height: 1.8; }
.paper li { margin-bottom: 0.4rem; }
.paper a { color: var(--copper); text-decoration: none; border-bottom: 1px solid rgba(212,165,116,0.3); transition: border-color 0.3s; }
.paper a:hover { border-color: var(--copper); }
.paper strong { color: var(--text); }

/* ── FAQ ── */
#faq { padding: 8vh 0 10vh; background: var(--bg); position: relative; border-top: 1px solid rgba(255,255,255,0.03); }
.faq-title { font-size: 2.25rem; font-weight: 300; letter-spacing: -0.02em; margin-bottom: 3rem; text-align: center; color: var(--text); }
.faq-list { max-width: 800px; margin: 0 auto; display: flex; flex-direction: column; gap: 2rem; }
.faq-item { 
  padding: 2rem; 
  background: var(--glass); 
  border: 1px solid var(--glass-border); 
  border-radius: 8px; 
  backdrop-filter: blur(10px);
}
.faq-item h4 { font-family: var(--sans); font-size: 1.15rem; font-weight: 600; color: var(--copper); margin-bottom: 0.75rem; letter-spacing: -0.01em; }
.faq-item p { font-size: 0.95rem; color: var(--text-muted); line-height: 1.6; }

/* ── FOOTER ── */
#footer { padding: 4rem 0 3rem 0; text-align: center; border-top: 1px solid rgba(255,255,255,0.03); background: var(--bg); }
.footer-inner { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 1.5rem; }
.footer-brand { font-family: var(--mono); font-size: 0.8rem; letter-spacing: 0.15em; color: var(--text-muted); }
.footer-links { display: flex; gap: 1.5rem; font-size: 0.8rem; }
.footer-links a { color: var(--text-muted); text-decoration: none; transition: color 0.2s var(--ease); opacity: 0.8; }
.footer-links a:hover { color: var(--copper); opacity: 1; }
.footer-copy { font-size: 0.75rem; color: var(--text-muted); opacity: 0.5; }

/* ── REVEALS ── */
.reveal { opacity: 0; transform: translateY(30px); transition: all 1s var(--ease); }
.reveal.visible { opacity: 1; transform: translateY(0); }

/* ── ERROR 404 ── */
.error-page { display: flex; align-items: center; justify-content: center; min-height: 100vh; text-align: center; }
.error-code { font-size: clamp(4rem, 12vw, 8rem); font-weight: 300; color: var(--copper); letter-spacing: -0.04em; line-height: 1; margin-bottom: 0.5rem; }
.error-desc { font-size: 1.1rem; color: var(--text-muted); margin-bottom: 2rem; }

/* ── RESPONSIVE ── */
@media (max-width: 768px) {
  .nav-inner { padding: 0 1rem; }
  #hero { padding: 0 1rem; }
  .products-grid { grid-template-columns: 1fr; }
  .footer-inner { flex-direction: column; gap: 1rem; }
  .paper { padding: 2rem 1.5rem; }
  .article-container { padding: 0 1rem; }
}

/* The #hero half of the portrait rule. It has to sit AFTER the max-width:768px block:
   a media query adds no specificity, so declared earlier it would lose to the plain
   `#hero { align-items: center; padding: 0 2rem }` above. Homepage only -- the legal
   and article pages have no hero. */
@media (max-aspect-ratio: 4/5) {
  .hero-page #hero { align-items: flex-end; padding-bottom: 13vh; }
}
