/* ============================================================
   Illustratus Solutions — shared stylesheet
   Design tokens defined once, used everywhere.
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,400;0,9..144,500;0,9..144,600;1,9..144,400;1,9..144,500&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&family=Poppins:wght@200;300;400&display=swap');

:root {
  /* --- Color tokens (extracted from the Illustratus lamp product photo) --- */
  --ink: #F5F1E8;
  --paper: #000104;
  --paper-dim: #0D0906;
  --graphite: #B5B4B3;
  --graphite-light: #8A8078;
  --mark: #D9A55C;
  --mark-light: #F0D9A8;
  --mark-warm: #C08860;
  --mark-warm-dim: #705240;
  --line: #2A241C;
  --line-dark: #3A322A;
  --surface: #1A150D;

  /* --- Type tokens --- */
  --font-display: 'Fraunces', Georgia, serif;
  --font-body: 'Inter', -apple-system, sans-serif;
  --font-mono: 'JetBrains Mono', 'Courier New', monospace;
  --font-logo: 'Poppins', 'Inter', -apple-system, sans-serif;

  /* --- Layout tokens --- */
  /* Above 1120px the side space is entirely (viewport - cap) / 2,
     so widening the cap is the only thing that reduces it — the
     gutter sits inside the cap and never touches the outer edge.
     Splitting the difference between viewport and the old 1120px
     halves that side space at every width. The 1520px cap is
     chosen so the halving stays exact through 1920px — the
     widest mainstream desktop — and only bites on ultrawide,
     where unlimited growth would stretch rows past readable. */
  --max-width: min(1520px, calc((100vw + 1120px) / 2));
  --gutter: clamp(0.75rem, 1vw, 1.5rem);
  --radius: 3px;

  /* Lamp header metrics. Shared so the in-flow spacer can be built
     from exactly the same numbers as the header it stands in for —
     if these drift apart the page jumps on load. */
  --lamp-pad-top: clamp(1.25rem, 3.5vw, 2.75rem);
  --lamp-pad-bottom: clamp(0.25rem, 1.5vw, 1rem);
  --lamp-stage-h: clamp(125px, 19vw, 243px);
  /* Collapse/expand speed. Every part of the header animates on this
     one value so they stay locked together — tune here, not in the
     individual rules, or the pieces drift out of step. */
  --lamp-collapse: 0.18s;
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  font-family: var(--font-body);
  background: var(--paper);
  color: var(--ink);
  line-height: 1.6;
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--mark);
  text-decoration: none;
}

a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--mark);
  outline-offset: 3px;
}

img { max-width: 100%; display: block; }

.wrap {
  max-width: var(--max-width);
  margin: 0 auto;
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}

/* ============================================================
   Navigation
   ============================================================ */

.site-nav {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(0, 1, 4, 0.85);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}

.site-nav .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 76px;
}

.wordmark {
  font-family: var(--font-display);
  font-size: 1.35rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  display: flex;
  align-items: baseline;
  align-self: flex-start;
  gap: 0.5rem;
  background: linear-gradient(100deg, #E6BD8D, #FFF5C7, #E6BD8D);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.wordmark .mark-dot {
  color: var(--mark);
  font-size: 1.6rem;
  line-height: 0;
}

.wordmark-icon {
  height: 32px;
  width: auto;
  align-self: center;
  border-radius: 2px;
}

.nav-links {
  display: flex;
  gap: 2.25rem;
  align-items: center;
  list-style: none;
  margin: 0;
  padding: 0;
}

.nav-links a {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: var(--graphite);
  text-transform: lowercase;
  letter-spacing: 0.02em;
  transition: color 0.15s ease;
}

.nav-links a:hover,
.nav-links a[aria-current="page"] {
  color: var(--mark);
}

.nav-cta {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  padding: 0.5rem 1rem;
  border: 1px solid var(--ink);
  border-radius: var(--radius);
  color: var(--ink) !important;
  transition: background 0.15s ease, color 0.15s ease;
}

.nav-cta:hover {
  background: var(--ink);
  color: var(--paper) !important;
}

.nav-toggle {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.5rem;
}

.nav-toggle svg { display: block; }

@media (max-width: 780px) {
  .nav-links {
    position: fixed;
    top: 76px;
    left: 0;
    right: 0;
    background: var(--paper);
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    border-bottom: 1px solid var(--line);
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.25s ease;
  }
  .nav-links.open { max-height: 400px; }
  .nav-links li { width: 100%; }
  .nav-links a {
    display: block;
    padding: 1rem var(--gutter);
    width: 100%;
    border-bottom: 1px solid var(--line);
  }
  .nav-toggle { display: block; }
}

/* ============================================================
   Eyebrow labels (mono utility text)
   ============================================================ */

.eyebrow {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 1.25rem;
  color: var(--mark);
  text-transform: lowercase;
  letter-spacing: 0.03em;
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  margin-bottom: 1rem;
}

/* ============================================================
   Announcement banner — the "coming soon" line at the top of
   the page, promoted from a plain eyebrow into its own object.

   A separate class rather than a change to .eyebrow: the value
   cards, products and contact sections all still use .eyebrow
   as a quiet label, and this one needs to shout.

   Same mono face as before — only the frame, tracking, and the
   space around it changed.
   ============================================================ */

.announce {
  display: inline-flex;
  align-items: center;
  gap: 0.85em;
  /* generous below, so it reads as its own banner rather than a
     label attached to the headline underneath */
  /* Top margin is the only thing tuning the gap to the lamp above —
     the stage height sets the baseline, this adds the rest. Keeping
     it here rather than growing the stage means the lamp's own
     composition (bar-to-wordmark spacing) stays where you set it. */
  margin: 1.5rem auto 3rem;
  padding: 0.85rem clamp(1.4rem, 3vw, 2.4rem);
  border: 1px solid rgba(217, 165, 92, 0.45);
  border-radius: 999px;
  /* top-lit: brighter along the upper edge, as if the fixture
     above is falling on it like everything else on this page */
  background: linear-gradient(180deg,
    rgba(217, 165, 92, 0.14) 0%,
    rgba(217, 165, 92, 0.04) 100%);
  animation: announce-rise 0.7s ease-out 0.35s both,
             announce-glow 3.4s ease-in-out 1.05s infinite;
}

/* Sized to sit clearly under the h1 below it — loud, but still
   second in the hierarchy, never competing with the headline. */
.announce-text {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: clamp(1.05rem, 3vw, 2.15rem);
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.26em;
  /* reclaim the trailing letter-space so the pill's padding
     stays even on both sides */
  margin-right: -0.26em;

  /* The flash: a highlight travelling across the glyphs, which is
     the lamp motif rather than a generic shimmer. Wider than the
     text (background-size) so there is somewhere to travel from. */
  background: linear-gradient(100deg,
    #B4762A 0%,
    #E0A445 18%,
    #FFD48A 38%,
    #FFF8E6 50%,
    #FFD48A 62%,
    #E0A445 82%,
    #B4762A 100%);
  background-size: 260% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: announce-sweep 4.6s linear infinite;
}

/* The live dot, scaled to the bigger type. */
.announce-dot {
  width: 0.42em;
  height: 0.42em;
  font-size: clamp(1.05rem, 3vw, 2.15rem);
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--mark-light);
  animation: announce-pulse 2.6s ease-out infinite;
}

@keyframes announce-sweep {
  from { background-position: 160% 0; }
  to   { background-position: -60% 0; }
}

@keyframes announce-glow {
  0%, 100% { box-shadow: 0 0 26px rgba(217, 165, 92, 0.14),
                         inset 0 1px 0 rgba(255, 244, 220, 0.10); }
  50%      { box-shadow: 0 0 54px rgba(217, 165, 92, 0.34),
                         inset 0 1px 0 rgba(255, 244, 220, 0.16); }
}

@keyframes announce-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(240, 217, 168, 0.6); }
  70%  { box-shadow: 0 0 0 14px rgba(240, 217, 168, 0); }
  100% { box-shadow: 0 0 0 0 rgba(240, 217, 168, 0); }
}

@keyframes announce-rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* The global reduced-motion rule collapses animations to their end
   frame, which for the sweep would park the gradient off to one side
   and leave the text muddy. Pin it mid-sweep instead, so a still
   banner still reads as lit. */
@media (prefers-reduced-motion: reduce) {
  .announce,
  .announce-dot { animation: none; }
  .announce-text {
    animation: none;
    background-position: 50% 0;
  }
  .announce { box-shadow: 0 0 30px rgba(217, 165, 92, 0.18); }
}

/* ============================================================
   Typography — headings & body
   ============================================================ */

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 500;
  color: var(--ink);
  line-height: 1.15;
  margin: 0;
  letter-spacing: -0.01em;
}

h1 { font-size: clamp(2.4rem, 5.5vw, 4.2rem); font-weight: 400; }
h2 { font-size: clamp(1.8rem, 3.2vw, 2.6rem); }
h3 { font-size: 1.3rem; }

p { margin: 0 0 1rem; color: var(--graphite); }
p:last-child { margin-bottom: 0; }

.lede {
  font-size: 1.2rem;
  color: var(--graphite);
  max-width: 42ch;
  line-height: 1.55;
}

/* ============================================================
   The signature "mark" motif — a hand-annotation SVG underline
   used to strike/emphasize a word in headlines.
   ============================================================ */

.annotated {
  position: relative;
  white-space: nowrap;
}

.annotated svg {
  position: absolute;
  left: -2%;
  bottom: -0.28em;
  width: 104%;
  height: 0.4em;
  overflow: visible;
}

.annotated svg path {
  fill: none;
  stroke: var(--mark);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-dasharray: 340;
  stroke-dashoffset: 340;
  animation: draw-mark 0.9s ease-out 0.5s forwards;
}

@keyframes draw-mark {
  to { stroke-dashoffset: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .annotated svg path {
    animation: none;
    stroke-dashoffset: 0;
  }
}

.strike {
  position: relative;
  color: var(--graphite-light);
}

.strike::after {
  content: '';
  position: absolute;
  left: -2%;
  right: -2%;
  top: 52%;
  height: 2px;
  background: var(--mark-warm);
  transform: scaleX(0);
  transform-origin: left;
  animation: draw-strike 0.4s ease-out 1.5s forwards;
}

@keyframes draw-strike {
  to { transform: scaleX(1); }
}

@media (prefers-reduced-motion: reduce) {
  .strike::after { animation: none; transform: scaleX(1); }
}

/* ============================================================
   Buttons
   ============================================================ */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-family: var(--font-mono);
  font-size: 0.88rem;
  padding: 0.85rem 1.5rem;
  border-radius: var(--radius);
  border: 1px solid var(--ink);
  transition: background 0.15s ease, color 0.15s ease, transform 0.15s ease;
  cursor: pointer;
}

.btn-primary {
  background: var(--ink);
  color: var(--paper) !important;
}

.btn-primary:hover {
  background: var(--mark);
  border-color: var(--mark);
  transform: translateY(-1px);
}

.btn-ghost {
  background: transparent;
  color: var(--ink) !important;
}

.btn-ghost:hover {
  background: var(--ink);
  color: var(--paper) !important;
}

/* ============================================================
   Sections
   ============================================================ */

section { padding: clamp(4rem, 9vw, 7rem) 0; }

.section-dark {
  background: var(--paper-dim);
}

.section-dark .eyebrow { color: var(--mark-light); }
.section-dark .eyebrow::before { background: var(--mark-light); }

.section-dim { background: var(--paper-dim); }

/* ============================================================
   Hero
   ============================================================ */

.hero {
  padding-top: clamp(4rem, 10vw, 8rem);
  padding-bottom: clamp(3rem, 7vw, 5rem);
}

.hero h1 { max-width: 16ch; }

.hero-sub {
  margin-top: 1.75rem;
  display: flex;
  gap: 1.25rem;
  align-items: flex-start;
  flex-wrap: wrap;
}

.hero-actions {
  margin-top: 2.5rem;
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
}

.hero-split {
  display: grid;
  grid-template-columns: 1fr 1.15fr;
  gap: 4rem;
  align-items: start;
}

.hero-split h2 { margin-bottom: 0.5rem; }

.hero-split .values-grid { margin-top: 1.75rem; }

@media (max-width: 900px) {
  .hero-split {
    grid-template-columns: 1fr;
    gap: 3rem;
  }
}

/* ============================================================
   Values grid (four commitments — even grid, no numbering,
   since these are simultaneous, not sequential)
   ============================================================ */

.values-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  margin-top: 3rem;
}

.value-card {
  background: var(--surface);
  padding: 2.75rem;
}

.value-card .eyebrow { margin-bottom: 1.5rem; }

.value-card h3 { margin-bottom: 0.75rem; }

.value-card p { font-size: 0.97rem; }

@media (max-width: 720px) {
  .values-grid { grid-template-columns: 1fr; }
  .value-card { padding: 2rem; }
}

/* ============================================================
   Product cards
   ============================================================ */

.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1.75rem;
  margin-top: 3rem;
}

.product-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 2rem;
  background: var(--surface);
  display: flex;
  flex-direction: column;
  transition: border-color 0.15s ease, transform 0.15s ease;
}

.product-card:hover {
  border-color: var(--mark);
  transform: translateY(-2px);
}

.product-icon {
  width: 44px;
  height: 44px;
  border-radius: var(--radius);
  background: var(--paper-dim);
  border: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 1.5rem;
  color: var(--mark);
}

.status-badge {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  padding: 0.28rem 0.65rem;
  border-radius: 999px;
  display: inline-block;
  margin-bottom: 1rem;
  width: fit-content;
  text-transform: lowercase;
  letter-spacing: 0.02em;
}

.status-live {
  background: rgba(217, 165, 92, 0.14);
  color: var(--mark);
  border: 1px solid rgba(217, 165, 92, 0.32);
}

.status-progress {
  background: rgba(192, 136, 96, 0.12);
  color: var(--mark-warm);
  border: 1px solid rgba(192, 136, 96, 0.3);
}

.status-planned {
  background: transparent;
  color: var(--graphite-light);
  border: 1px solid var(--line);
}

/* "coming soon" — the one badge meant to pull the eye. The other three
   are tinted outlines that differ only by hue, which reads as a set;
   this one inverts to a solid fill instead, the same move .btn-primary
   makes for the primary action. Sitting beside a .status-progress pill
   on the same row, fill-vs-outline is what separates them at a glance —
   a hue shift alone would not. Gold on --paper measures ~9:1, so the
   small mono type stays well clear of WCAG AA. */
.status-soon {
  background: var(--mark);
  border: 1px solid var(--mark);
  color: var(--paper);
  font-weight: 500;
  box-shadow: 0 0 14px rgba(217, 165, 92, 0.28);
}

/* Icon and status pill share a row, pill pushed to the right edge.
   Purely additive — products.html has no .product-card-top wrapper,
   so its stacked layout is untouched. The children's own bottom
   margins are zeroed and the spacing moved onto the row, otherwise
   the taller of the two would set the gap below. */
.product-card-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.product-card-top .product-icon,
.product-card-top .status-badge {
  margin-bottom: 0;
  flex-shrink: 0;
}

/* Holds one or more marks as a single unit, so space-between still
   pushes the status pill to the far edge regardless of how many. */
.product-marks {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
}

.product-card h3 { margin-bottom: 0.6rem; }

.product-card p { flex-grow: 1; font-size: 0.95rem; }

/* Estimated release line. Selectors are .product-card-scoped because
   `.product-card p` (0,1,1) would otherwise outrank a lone class and
   win the font-size and flex-grow. flex-grow:0 keeps the description
   above as the element that absorbs slack, so links stay bottom-
   aligned across cards of differing text length. */
.product-card .product-release {
  flex-grow: 0;
  margin: 1.5rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  color: var(--graphite-light);
}

.product-card .product-release span { color: var(--mark); }

/* Tighten up when a release line precedes the link, so the two
   1.5rem margins don't stack into a 3rem hole. */
.product-card .product-release + .product-link { margin-top: 0.75rem; }

.product-card .product-link {
  margin-top: 1.5rem;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
}

.product-link.disabled {
  color: var(--graphite-light);
  pointer-events: none;
}

/* ============================================================
   Footer
   ============================================================ */

.site-footer {
  background: var(--paper-dim);
  color: var(--graphite);
  padding: 3.5rem 0 2.5rem;
}

.footer-top {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 2.5rem;
  padding-bottom: 2.5rem;
  border-bottom: 1px solid var(--line-dark);
}

.footer-wordmark {
  font-family: var(--font-display);
  font-size: 1.2rem;
  background: linear-gradient(100deg, #E6BD8D, #FFF5C7, #E6BD8D);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.footer-links {
  display: flex;
  gap: 3rem;
  flex-wrap: wrap;
}

.footer-col h4 {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--graphite-light);
  text-transform: lowercase;
  font-weight: 500;
  margin-bottom: 1rem;
  letter-spacing: 0.02em;
}

.footer-col ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.footer-col a {
  color: var(--graphite);
  font-size: 0.9rem;
}

.footer-col a:hover { color: var(--mark-light); }

.footer-bottom {
  padding-top: 1.75rem;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  font-size: 0.82rem;
  font-family: var(--font-mono);
  color: var(--graphite-light);
}

/* ============================================================
   Page header (non-home pages)
   ============================================================ */

.page-header {
  padding-top: clamp(3.5rem, 8vw, 6rem);
  padding-bottom: 2rem;
}

/* 20ch resolves to roughly 10em in Fraunces, but the headline
   needs 15.7em to sit on one line — measured, and a pure font
   ratio, so it holds at every clamp() step. 18em clears that
   with slack for a slightly longer headline, while still
   stopping the block from spanning the full container on wide
   displays. Below ~615px the clamp's 2.4rem floor outruns the
   viewport and it wraps, which is what you want on a phone. */
.page-header h1 {
  max-width: 18em;
  /* 4px off the global h1 scale. Subtracting from the clamp rather
     than rewriting its three values keeps the responsive curve and
     stays 4px smaller at every viewport. */
  font-size: calc(clamp(2.4rem, 5.5vw, 4.2rem) - 4px);
}
/* Scoped to .page-header on purpose. The global .lede keeps its
   42ch reading measure — about/products/contact all use it for
   real paragraphs, where a 34em line is far past comfortable.
   Here the ledes are display copy sitting under the headline,
   so they follow the headline's no-wrap treatment instead. The
   longer of the two measures 32.17em; 34em clears it. */
.page-header .lede {
  margin-top: 1.25rem;
  max-width: 34em;
}

/* ============================================================
   Two-column feature rows
   ============================================================ */

.feature-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4rem;
  align-items: center;
}

.feature-row + .feature-row { margin-top: 5rem; }

.feature-row.reverse .feature-visual { order: 2; }

@media (max-width: 780px) {
  .feature-row {
    grid-template-columns: 1fr;
    gap: 2rem;
  }
  .feature-row.reverse .feature-visual { order: 0; }
}

.feature-visual {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  aspect-ratio: 4 / 3;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem;
}

/* ============================================================
   Contact form
   ============================================================ */

.contact-layout {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 4rem;
}

@media (max-width: 780px) {
  .contact-layout { grid-template-columns: 1fr; }
}

.form-group { margin-bottom: 1.4rem; }

.form-group label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--graphite);
  margin-bottom: 0.5rem;
}

.form-group input,
.form-group textarea,
.form-group select {
  width: 100%;
  font-family: var(--font-body);
  font-size: 1rem;
  padding: 0.8rem 1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
  transition: border-color 0.15s ease;
}

.form-group input:focus,
.form-group textarea:focus,
.form-group select:focus {
  outline: none;
  border-color: var(--mark);
}

.form-group textarea { resize: vertical; min-height: 120px; }

.contact-info-block {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 2rem;
  margin-bottom: 1.5rem;
}

.contact-info-block h3 { margin-bottom: 0.5rem; font-size: 1.05rem; }

/* Honeypot wrapper — off-screen rather than display:none or
   visibility:hidden, both of which bots commonly detect and skip. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Submission result. min-height reserves the line so the button
   doesn't jump when a message appears. */
.form-status {
  margin: 1rem 0 0;
  min-height: 1.4em;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  color: var(--graphite);
}

.form-status.is-ok { color: var(--mark); }
.form-status.is-error { color: #E08A6A; }   /* warm enough for the palette, clearly not a success */

/* Live character counter under the message field. */
.char-count {
  margin: 0.4rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  color: var(--graphite-light);
  text-align: right;
}

/* Warm up as the limit approaches, so running out isn't a surprise. */
.char-count.is-near { color: var(--mark); }
.char-count.is-full { color: var(--mark-light); }

/* ============================================================
   Contact section — every vertical value halved from the site
   default. Scoped to #contact so the shared section rhythm on
   about/products/contact is left alone.
   ============================================================ */

#contact {
  padding-top: clamp(2rem, 4.5vw, 3.5rem);
  padding-bottom: clamp(2rem, 4.5vw, 3.5rem);
}

#contact .eyebrow { margin-bottom: 0.5rem; }
#contact .lede { margin-top: 0.375rem; }
#contact .form-group { margin-bottom: 0.7rem; }
#contact .form-group textarea { min-height: 96px; }

/* ============================================================
   Lamp header — the Illustratus fixture drawn in CSS rather
   than shipped as a raster. A PNG's light noise ends at the
   image edge and never quite matches --paper; here every layer
   is transparent and only adds light, so the falloff resolves
   into whatever the page background happens to be.

   Layer order, back to front:
     .lamp-wall     ambient wash on the wall behind the fixture
     .lamp-beam     the clipped cone spilling downward
     .lamp-pool     the pool of light the wordmark sits in
     .lamp-bar      the fixture body (+ vent slot, + emitter)
     .lamp-wordmark the lockup
   ============================================================ */

/* ── Sticky, shrinking header ───────────────────────────────────
   position:fixed, not sticky. A sticky header stays in flow, so
   shrinking it reflows the page, which moves the scroll position,
   which re-crosses the threshold that set the size — the header
   grows into its own trigger and oscillates. That is the shake, and
   it only showed going small-to-large because only growing pushes
   content down past the threshold again.

   Out of flow, its height affects nothing. .lamp-header-spacer holds
   the expanded height in flow permanently, so no scroll position ever
   moves and the trigger stays stable.
   ─────────────────────────────────────────────────────────────── */

.lamp-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  overflow: hidden;
  background: var(--paper);
  padding: var(--lamp-pad-top) 0 var(--lamp-pad-bottom);
  border-bottom: 1px solid transparent;
  /* ease-out, not ease: it covers most of the distance up front, which
     reads as quicker than the same duration with a slow start. */
  transition: padding var(--lamp-collapse) ease-out,
              border-color var(--lamp-collapse) ease-out;
}

/* Constant height — never animated. This is the piece that keeps the
   page still while the header above it resizes. */
.lamp-header-spacer {
  height: calc(var(--lamp-pad-top) + var(--lamp-stage-h) + var(--lamp-pad-bottom));
}

/* ── Compact state ──────────────────────────────────────────────
   Collapses to a slim lit bar so a ~230px header isn't parked over a
   quarter of a phone screen. The atmospheric layers go: they need
   vertical room to fall off and no longer have any.
   ─────────────────────────────────────────────────────────────── */

.lamp-stage    { transition: height var(--lamp-collapse) ease-out; }
.lamp-plate    { transition: top var(--lamp-collapse) ease-out; }
.lamp-wordmark { transition: font-size var(--lamp-collapse) ease-out; }
.lamp-wall,
.lamp-beam,
.lamp-pool     { transition: opacity var(--lamp-collapse) ease-out; }

.lamp-header.is-compact {
  padding-top: 0.45rem;
  padding-bottom: 0.55rem;
  border-bottom-color: var(--line);
}

.lamp-header.is-compact .lamp-stage {
  height: 80px;
  --bar-top: 10%;
  --bar-w: clamp(180px, 40%, 380px);
}

/* The fixture keeps its pixel height while the stage shrinks, so a
   percentage that reads fine expanded leaves the wordmark almost
   touching the bar once collapsed. Pushed well down to restore the
   gap — roughly doubled from where it was. */
.lamp-header.is-compact .lamp-plate { top: 50%; }

.lamp-header.is-compact .lamp-wordmark {
  font-size: clamp(0.68rem, 1.5vw, 1rem);
}

.lamp-header.is-compact .lamp-wall,
.lamp-header.is-compact .lamp-beam,
.lamp-header.is-compact .lamp-pool { opacity: 0; }

/* The stage was scaled by k = 0.848 to close 25% of the gap
   between the wordmark and the section below. Every height term
   is multiplied by k and every top-anchored percentage divided
   by it, so the fixture and plate hold identical absolute
   positions at every viewport — only the empty falloff room
   below the lockup shrinks. Keep that relationship if you
   retune: change one, change the other. */
.lamp-stage {
  --bar-w: clamp(220px, 54%, 640px);
  --bar-h: clamp(10px, 1.6vw, 19px);
  --bar-top: 16.75%;

  position: relative;
  width: 100%;
  max-width: 1040px;
  height: var(--lamp-stage-h);
  margin: 0 auto;
}

/* --- Ambient wash: the wall catching light around the fixture --- */
.lamp-wall {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 62% 78% at 50% 14%,
    rgba(255, 172, 92, 0.13) 0%,
    rgba(214, 128, 62, 0.06) 34%,
    rgba(150, 84, 40, 0.02) 55%,
    transparent 74%);
  mix-blend-mode: screen;
  animation: lamp-warm 1.1s ease-out both;
}

/* --- Beam: a trapezoid cone spilling down from the emitter.
       The blur has to sit on the WRAPPER, not on the clipped
       shape: CSS applies filter before clip-path, so blurring
       the clipped element just yields a soft fill inside a
       razor-sharp polygon. Blurring the parent instead softens
       the clip itself, which is what makes it read as light.
       Wrapper is 150% of the stage so the cone can keep
       spreading past the wordmark before it runs out of box. --- */
.lamp-beam {
  position: absolute;
  top: calc(var(--bar-top) + var(--bar-h));
  left: -25%;
  width: 150%;
  height: 80%;
  filter: blur(clamp(20px, 3.4vw, 46px));
  mix-blend-mode: screen;
  animation: lamp-warm 1.1s ease-out both;
}

.lamp-beam span {
  position: absolute;
  inset: 0;
  display: block;
  clip-path: polygon(34.5% 0%, 65.5% 0%, 88% 100%, 12% 100%);
  background: linear-gradient(to bottom,
    rgba(255, 207, 152, 0.52) 0%,
    rgba(250, 182, 118, 0.30) 14%,
    rgba(228, 146, 84, 0.145) 32%,
    rgba(188, 110, 56, 0.06) 54%,
    rgba(134, 74, 36, 0.018) 76%,
    transparent 93%);
}

/* --- Pool: the soft landing of the beam, centered on the type --- */
.lamp-pool {
  position: absolute;
  left: 50%;
  top: 16.77%;
  transform: translateX(-50%);
  width: 108%;
  height: 76.21%;
  background: radial-gradient(ellipse at 50% 48%,
    rgba(255, 190, 122, 0.17) 0%,
    rgba(226, 146, 80, 0.075) 34%,
    rgba(176, 100, 48, 0.02) 58%,
    transparent 76%);
  filter: blur(clamp(10px, 1.8vw, 26px));
  mix-blend-mode: screen;
  animation: lamp-warm 1.1s ease-out both;
}

/* --- Emitter: the lit underside of the bar. Deliberately a
       sibling that paints BEFORE .lamp-bar rather than a child
       of it — the housing has to occlude the upward half of the
       glow, otherwise the bloom washes across the bar's face
       and the fixture reads as a pale slab instead of a dark
       one with a lit edge. Only the few px below the housing
       show. The horizontal fade at both ends keeps the strip
       from ending in a hard cap where the housing wraps. --- */
.lamp-emitter {
  position: absolute;
  top: calc(var(--bar-top) + var(--bar-h) - 1px);
  left: 50%;
  transform: translateX(-50%);
  width: calc(var(--bar-w) - 3px);
  height: clamp(2.5px, 0.4vw, 5px);
  border-radius: 999px;
  background: linear-gradient(to right,
    rgba(255, 216, 160, 0) 0%,
    rgba(255, 233, 197, 0.85) 5%,
    #FFF1DC 30%,
    #FFFAF0 50%,
    #FFF1DC 70%,
    rgba(255, 233, 197, 0.85) 95%,
    rgba(255, 216, 160, 0) 100%);
  box-shadow:
    0 0 6px rgba(255, 228, 184, 0.95),
    0 0 16px rgba(255, 198, 138, 0.72),
    0 0 38px rgba(252, 164, 92, 0.44),
    0 0 78px rgba(232, 138, 66, 0.24);
  animation: lamp-strike 1.1s ease-out both;
}

/* --- Fixture body --- */
.lamp-bar {
  position: absolute;
  top: var(--bar-top);
  left: 50%;
  transform: translateX(-50%);
  width: var(--bar-w);
  height: var(--bar-h);
  border-radius: calc(var(--bar-h) / 2.4);
  background: linear-gradient(to bottom,
    #3B3833 0%,
    #211E19 12%,
    #13100B 42%,
    #0A0805 76%,
    #060401 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 248, 238, 0.11),
    inset 0 -1px 0 rgba(255, 200, 144, 0.22),
    0 2px 18px rgba(0, 0, 0, 0.6);
}

/* Vent / indicator slot on the face of the bar */
.lamp-bar::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 4.6%;
  height: 20%;
  min-height: 2px;
  border-radius: 999px;
  background: rgba(196, 192, 186, 0.42);
  box-shadow: 0 0 5px rgba(255, 222, 176, 0.22);
}

/* --- Plate: the black card the wordmark sits on. Normal blend,
       not screen, so it actually occludes the beam behind it —
       the type needs to sit on black, not on lit wall. Hard
       edges by design: no blur, so it reads as a cut card
       against the lit wall rather than a soft shadow.

       No width of its own: an absolutely positioned box with
       width:auto shrink-wraps its content, so the card is only
       ever as wide as the lockup plus its padding. The padding
       lives on .lamp-wordmark, where `em` resolves against the
       lockup's own font-size and therefore tracks it at every
       breakpoint. --- */
.lamp-plate {
  position: absolute;
  left: 50%;
  top: 41.91%;
  transform: translateX(-50%);
  background: var(--paper);
  animation: lamp-warm 1.1s ease-out both;
}

/* --- Wordmark --- */
.lamp-wordmark {
  --tracking: clamp(0.1em, 0.46vw, 0.3em);

  margin: 0;
  /* Asymmetric top/bottom on purpose. The lockup is all caps
     with no descenders, so its ink stops at the baseline while
     the line box still reserves Poppins' full descent below it.
     Even padding therefore renders the caps visibly high; the
     extra ~0.023em on top re-centers the ink itself. In em, so
     it holds at every clamp() step. */
  padding: 0.303em 0.62em 0.257em;
  white-space: nowrap;

  font-family: var(--font-logo);
  font-weight: 300;
  font-size: clamp(0.82rem, 3.15vw, 2.45rem);
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: var(--tracking);
  /* letter-spacing appends a trailing gap after the last glyph.
     Inside a shrink-wrapped card that gap becomes real box
     width, hanging extra black off the right edge — so reclaim
     it and the lockup sits centered in its own padding. */
  margin-right: calc(-1 * var(--tracking));

  /* Top layer lights the caps and drops the baseline, as if the
     glyphs themselves are catching the fixture above them.
     Bottom layer is the gold -> silver run across the lockup. */
  background:
    linear-gradient(to bottom,
      rgba(255, 246, 230, 0.30) 0%,
      rgba(255, 246, 230, 0.05) 38%,
      rgba(0, 0, 0, 0.10) 72%,
      rgba(0, 0, 0, 0.28) 100%),
    linear-gradient(96deg,
      #B4762A 0%,
      #E8A845 8%,
      #FFD592 20%,
      #F5BE64 31%,
      #E9AE52 42%,
      #C9B48C 49%,
      #9BA1A8 55%,
      #C4CAD1 66%,
      #EDF1F5 80%,
      #B2B8BF 92%,
      #8A9097 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;

  /* drop-shadow, not text-shadow: with a transparent fill a
     text-shadow shows straight through the glyphs */
  filter:
    drop-shadow(0 0 9px rgba(255, 196, 130, 0.28))
    drop-shadow(0 0 28px rgba(230, 148, 76, 0.16));
  animation: lamp-warm 1.1s ease-out 0.12s both;
}

/* Base state of every keyframe below is "lamp on", so if
   animations are stripped out entirely the fixture still reads
   as lit rather than dark. */
@keyframes lamp-warm {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Keeps translateX(-50%) in every frame — the emitter is
   centered by transform, so a bare scaleX() would drop it. */
@keyframes lamp-strike {
  0%   { opacity: 0;    transform: translateX(-50%) scaleX(0.72); }
  55%  { opacity: 1;    transform: translateX(-50%) scaleX(1); }
  70%  { opacity: 0.82; transform: translateX(-50%) scaleX(1); }
  100% { opacity: 1;    transform: translateX(-50%) scaleX(1); }
}

/* Below ~560px the tracking has to give before the lockup does,
   or it outruns the beam and clips at the stage edge. */
@media (max-width: 560px) {
  .lamp-stage {
    --bar-w: clamp(200px, 72%, 420px);
    --bar-top: 19.82%;   /* same /k compensation as the base rule */
  }
  .lamp-wordmark {
    --tracking: 0.16em;
    font-size: clamp(0.8rem, 4.9vw, 1.35rem);
  }
}

/* ============================================================
   Utility
   ============================================================ */

.center-text { text-align: center; }
.mt-lg { margin-top: 3rem; }

.divider {
  height: 1px;
  background: var(--line);
  border: none;
  margin: 0;
}

.section-dark .divider { background: var(--line-dark); }
