/* ============================================================================
   sawwave.css — the sawwave.works design system.

   One stylesheet shared by the homepage shell AND every hosted app (buzz,
   wedding, news, …) so the whole domain reads as one place. Apps link it
   cross-origin: <link rel="stylesheet" href="https://sawwave.works/sawwave.css">.

   Sections:
     1. Tokens            6. Audio dock / waveform / toast
     2. Base + reset      7. View: home
     3. Pre-shell guard   8. View: about
     4. Rain gutters      9. View: guestbook
     5. Shell + chrome   10. App (iframe) view   11. Responsive
   See STYLE_GUIDE.md for usage + the framing/persistence contract.
   ========================================================================== */

/* ── 1. Tokens ───────────────────────────────────────────────────────────── */
:root {
  --bg: #000000;
  --bg-elevated: #020602;
  --fg: #00ff41;
  --fg-soft: #00cc33;
  --muted: #007a1f;
  --border: #003d10;
  --err: #ff6b6b;
  --glow: 0 0 4px rgba(0, 255, 65, 0.45);
  --glow-strong: 0 0 6px rgba(0, 255, 65, 0.6), 0 0 12px rgba(0, 255, 65, 0.3);
  --content-w: 720px;
  --font: "VT323", "Courier New", "Consolas", monospace;
  --pad-y: clamp(1.5rem, 4vw, 3rem);
  --pad-x: clamp(1rem, 5vw, 2rem);
}

/* ── 2. Base + reset ─────────────────────────────────────────────────────── */
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { background: var(--bg); color: var(--fg); }
body {
  font-family: var(--font);
  font-size: 22px;
  line-height: 1.45;
  min-height: 100vh;
  padding: var(--pad-y) var(--pad-x);
  text-shadow: var(--glow);
  overflow-x: hidden;
}
a { color: var(--fg); text-decoration: none; }
::selection { background: var(--fg); color: #000; text-shadow: none; }
@keyframes blink { 50% { opacity: 0; } }

/* ── 3. Pre-shell guard ──────────────────────────────────────────────────────
   shell.js injects all chrome, then sets body.shell-ready. Until then, hide the
   bare #view-content so there's no flash of unstyled content before the intro. */
html.js body:not(.shell-ready) #view-content { opacity: 0; }

/* ── 4. Rain gutters (persistent) ────────────────────────────────────────── */
canvas.rain {
  position: fixed;
  top: 0;
  pointer-events: none;
  z-index: 1;
  opacity: 0.7;
}
#rain-left { left: 0; }
#rain-right { right: 0; }

/* ── 5. Shell + chrome ───────────────────────────────────────────────────── */
.shell {
  max-width: var(--content-w);
  margin: 0 auto;
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: clamp(2.5rem, 6vw, 4rem);
  min-height: calc(100vh - clamp(3rem, 8vw, 6rem));
}
/* Content fades in from black during the intro; shell.js adds .revealed.
   Keep these two at equal specificity so source order lets .revealed win. */
.js .shell { opacity: 0; }
.js .shell.revealed { opacity: 1; transition: opacity 700ms ease; }

header.shell-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  border-bottom: 1px solid var(--fg);
  padding-bottom: 0.6rem;
  gap: 2rem;
  flex-wrap: wrap;
}
.mark {
  font-size: clamp(1.6rem, 3vw, 2.1rem);
  text-shadow: var(--glow-strong);
  letter-spacing: 0.02em;
}
.mark a { color: var(--fg); }
.mark .caret { animation: blink 1s steps(2) infinite; color: var(--fg); }
nav.shell-nav { display: flex; gap: 1.5rem; font-size: 1.05rem; align-items: baseline; }
nav.shell-nav a {
  color: var(--fg-soft);
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
  transition: color .15s, border-color .15s, text-shadow .15s;
}
nav.shell-nav a:hover,
nav.shell-nav a.active {
  color: var(--fg);
  border-bottom-color: var(--fg);
  text-shadow: var(--glow-strong);
}

#view { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; }
#view-content { min-width: 0; }

footer.shell-footer {
  border-top: 1px solid var(--fg);
  padding-top: 0.6rem;
  display: flex;
  justify-content: space-between;
  color: var(--fg-soft);
  flex-wrap: wrap;
  gap: 1rem;
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
footer.shell-footer a { color: var(--fg-soft); }
footer.shell-footer a:hover { color: var(--fg); text-shadow: var(--glow-strong); }

.hitcounter { display: inline-flex; align-items: center; gap: 0.5ch; }
.hitcounter .hc-label { color: var(--muted); }
.hitcounter .hc-digits {
  color: var(--fg);
  background: #001a05;
  border: 1px solid var(--border);
  padding: 0.05rem 0.45rem;
  letter-spacing: 0.18em;
  font-feature-settings: "tnum" 1;
  text-shadow: var(--glow-strong);
  box-shadow: inset 0 0 6px rgba(0, 255, 65, 0.15);
}

/* Intro overlay: two opaque rain "curtains" that part to the gutters while the
   shell fades in behind the opening center. Background is transparent so the
   center reveals the fading content (the fix for "fade in from black"). */
#intro {
  position: fixed; inset: 0; z-index: 50;
  pointer-events: none; background: transparent; display: none;
}
html.js #intro { display: block; }
#intro.done { display: none; }
.intro-rain {
  position: absolute; top: 0; height: 100%;
  background: #000;            /* solid backing → the curtain is fully opaque */
  will-change: transform;
}
#intro-l { left: 0; }
#intro-r { right: 0; }

@media (prefers-reduced-motion: reduce) {
  .js .shell { opacity: 1; }
  html.js #intro { display: none; }
  .js .shell.revealed { transition: none; }
}

/* ── 6. Audio dock / waveform / toast ────────────────────────────────────── */
.audio-dock {
  position: fixed;
  left: max(0.9rem, calc(50vw - 360px + 0.9rem));
  bottom: 0.8rem;
  z-index: 4;
  display: flex; align-items: center; gap: 0.45rem;
  opacity: 0.4;
  transition: opacity .2s;
}
.audio-dock:hover { opacity: 1; }
.audio-btn {
  background: none; border: none; padding: 2px;
  color: var(--fg-soft); cursor: pointer; line-height: 0;
  transition: color .15s, filter .15s;
}
.audio-btn:hover { color: var(--fg); filter: drop-shadow(var(--glow)); }
.audio-btn:focus-visible { outline: 1px solid var(--fg); outline-offset: 3px; }
.audio-btn svg { width: 18px; height: 18px; display: block; }
.audio-dock.hint { animation: sndpulse 1.7s ease-in-out infinite; }
@keyframes sndpulse { 0%, 100% { opacity: 0.35; } 50% { opacity: 0.9; } }

.sawwave {
  position: fixed;
  bottom: 0.9rem;
  right: max(1.1rem, calc(50vw - 360px + 1.1rem));
  z-index: 3;
  pointer-events: none;
  display: block;
  width: 96px;
  height: 22px;
  image-rendering: pixelated;
}

.toast {
  position: fixed; left: 50%; bottom: 1.4rem;
  transform: translateX(-50%) translateY(0.6rem);
  z-index: 60;
  background: #001a05; border: 1px solid var(--border); color: var(--fg);
  padding: 0.45rem 0.9rem; font-size: 0.95rem; letter-spacing: 0.04em;
  text-shadow: var(--glow); box-shadow: inset 0 0 6px rgba(0, 255, 65, 0.15);
  opacity: 0; pointer-events: none;
  transition: opacity .25s, transform .25s;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ── 6b. Zen / rain-only mode ────────────────────────────────────────────────
   shell.js adds body.zen when the rain-mode toggle (or Esc) closes the content:
   the shell fades out, the rain canvases grow to fill the whole viewport, and the
   background track keeps playing. The dock floats above so you can toggle back. */
.js body.zen .shell { opacity: 0; pointer-events: none; }   /* fade reuses .shell.revealed's transition */
canvas.rain { transition: opacity .6s ease; }
body.zen canvas.rain { opacity: 0.9; }                      /* a touch fuller once it owns the screen */
body.zen .audio-dock { opacity: 0.75; }                     /* keep the exit control findable in the rain */
#zen-toggle[aria-pressed="true"] { color: var(--fg); filter: drop-shadow(var(--glow)); }
@media (prefers-reduced-motion: reduce) {
  .js body.zen .shell { transition: none; }
}

/* ── 7. View: home ───────────────────────────────────────────────────────── */
.hero { display: grid; gap: 1.5rem; }
.hero-label { color: var(--fg-soft); text-transform: uppercase; letter-spacing: 0.12em; font-size: 0.85rem; }
.hero-title {
  font-size: clamp(2.6rem, 7vw, 4.4rem);
  line-height: 1.05; font-weight: 400; letter-spacing: 0.005em;
  text-shadow: var(--glow-strong); margin-top: 0.3rem;
}
.hero-title em { font-style: normal; color: var(--fg); position: relative; }
.hero-title em::before { content: "["; opacity: 0.4; margin-right: 0.15em; }
.hero-title em::after  { content: "]"; opacity: 0.4; margin-left: 0.15em; }
.ascii-title {
  font-family: var(--font);
  font-size: clamp(0.78rem, 2.1vw, 1.35rem);
  line-height: 1.0; color: var(--fg); text-shadow: var(--glow-strong);
  white-space: pre; margin: 0.5rem 0 0.2rem; letter-spacing: 0; overflow-x: auto;
}
.ascii-title .ascii-row { display: block; }
.ascii-title .ascii-signal { color: var(--fg); text-shadow: 0 0 8px rgba(0, 255, 65, 0.7), 0 0 16px rgba(0, 255, 65, 0.35); }
.ascii-title .ascii-soft { color: var(--fg-soft); }
.ascii-title .ascii-bracket { color: var(--muted); }
.hero-blurb { color: var(--fg-soft); font-size: 1.15rem; line-height: 1.5; max-width: 58ch; }
.hero-blurb a { color: var(--fg); border-bottom: 1px solid var(--fg-soft); transition: border-color .15s, text-shadow .15s; }
.hero-blurb a:hover { text-shadow: var(--glow-strong); border-bottom-color: var(--fg); }

.projects-heading {
  display: flex; justify-content: space-between; align-items: baseline;
  border-bottom: 1px solid var(--fg); padding-bottom: 0.4rem;
  color: var(--fg); text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.9rem;
}
.project {
  display: grid; grid-template-columns: 3rem 1fr auto; align-items: baseline;
  gap: 1.25rem; padding: 1.1rem 0; border-bottom: 1px solid var(--border);
  color: var(--fg); transition: padding .2s, background .15s;
}
.project:hover { padding-left: 0.8rem; background: rgba(0, 255, 65, 0.04); }
.project:hover .project-title { text-shadow: var(--glow-strong); }
.project:hover .project-arrow { transform: translateX(0.4rem); text-shadow: var(--glow-strong); }
.project-num { color: var(--fg-soft); font-size: 0.95rem; letter-spacing: 0.05em; }
.project-body { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }
.project-title { font-size: clamp(1.5rem, 3vw, 1.9rem); line-height: 1.1; transition: text-shadow .15s; }
.project-meta { color: var(--fg-soft); font-size: 1rem; }
.project-arrow { font-size: 1.6rem; color: var(--fg-soft); transition: transform .2s, text-shadow .15s; }

.project-split {
  display: grid; grid-template-columns: 3rem 2fr 1fr; align-items: center;
  gap: 1.25rem; padding: 1.1rem 0; border-bottom: 1px solid var(--border); color: var(--fg);
}
.project-main {
  display: grid; grid-template-columns: 1fr auto; align-items: baseline; gap: 1.25rem;
  min-width: 0; color: var(--fg); transition: padding .2s, background .15s;
}
.project-main:hover { padding-left: 0.5rem; }
.project-main:hover .project-title { text-shadow: var(--glow-strong); }
.project-main:hover .project-arrow { transform: translateX(0.4rem); text-shadow: var(--glow-strong); }
.project-aside {
  align-self: stretch; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); color: var(--fg-soft);
  font-size: 1rem; letter-spacing: 0.1em; text-transform: uppercase; text-align: center;
  white-space: nowrap; transition: border-color .15s, color .15s, background .15s, text-shadow .15s;
}
.project-aside:hover { border-color: var(--fg); color: var(--fg); background: rgba(0, 255, 65, 0.05); text-shadow: var(--glow-strong); }
.project-aside-label::before { content: "["; opacity: 0.45; margin-right: 0.4em; }
.project-aside-label::after  { content: "]"; opacity: 0.45; margin-left: 0.4em; }

/* ── 8. View: about ──────────────────────────────────────────────────────── */
.title-block { display: grid; gap: 0.6rem; }
.title-label { color: var(--fg-soft); text-transform: uppercase; letter-spacing: 0.12em; font-size: 0.85rem; }
.title { font-size: clamp(2.2rem, 6vw, 3.6rem); line-height: 1.08; text-shadow: var(--glow-strong); }
.title em { font-style: normal; }
.title em::before { content: "["; opacity: 0.4; margin-right: 0.1em; }
.title em::after  { content: "]"; opacity: 0.4; margin-left: 0.1em; }
.layout { display: grid; grid-template-columns: 1fr; gap: clamp(2rem, 5vw, 3rem); }
@media (min-width: 760px) { .layout { grid-template-columns: 14rem minmax(0, 1fr); gap: 3rem; } }
aside { display: flex; flex-direction: column; gap: 1.2rem; }
aside .meta-block { display: flex; flex-direction: column; gap: 0.15rem; }
aside .label { color: var(--fg-soft); text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.75rem; }
aside .value { color: var(--fg); font-size: 1.05rem; }
aside a { color: var(--fg); border-bottom: 1px solid var(--fg-soft); transition: text-shadow .15s, border-color .15s; }
aside a:hover { text-shadow: var(--glow-strong); border-bottom-color: var(--fg); }
article { max-width: 64ch; }
article p { margin-bottom: 1.1em; color: var(--fg-soft); font-size: 1.05rem; line-height: 1.55; }
article p.lede { font-size: 1.25rem; color: var(--fg); margin-bottom: 1.4em; }
article p.lede::first-letter {
  font-size: 3em; float: left; line-height: 0.85; padding: 0.05em 0.15em 0 0;
  color: var(--fg); text-shadow: var(--glow-strong);
}
article a { color: var(--fg); border-bottom: 1px solid var(--fg-soft); transition: text-shadow .15s, border-color .15s; }
article a:hover { text-shadow: var(--glow-strong); border-bottom-color: var(--fg); }
.contact-row {
  margin-top: 2.5rem; padding-top: 1.2rem; border-top: 1px solid var(--fg);
  display: flex; gap: 2rem; flex-wrap: wrap; font-size: 1rem;
}
.contact-row a { border: none; }
.contact-row .label-inline { color: var(--fg-soft); text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.78rem; margin-right: 0.5rem; }

/* ── 9. View: guestbook ──────────────────────────────────────────────────── */
.intro-line { color: var(--fg-soft); font-size: 1.05rem; margin-top: 0.4rem; max-width: 52ch; }
#gb-form { display: grid; gap: 0.8rem; max-width: 48ch; margin-top: 1.8rem; }
#gb-form input, #gb-form textarea {
  background: var(--bg-elevated); border: 1px solid var(--border); color: var(--fg);
  font-family: inherit; font-size: 1rem; padding: 0.6rem 0.7rem; width: 100%; text-shadow: var(--glow);
}
#gb-form input::placeholder, #gb-form textarea::placeholder { color: var(--muted); }
#gb-form input:focus, #gb-form textarea:focus { outline: none; border-color: var(--fg); }
#gb-form textarea { resize: vertical; min-height: 5rem; }
.gb-actions { display: flex; align-items: center; gap: 1rem; }
.gb-submit {
  background: none; border: 1px solid var(--fg-soft); color: var(--fg);
  padding: 0.5rem 1.4rem; cursor: pointer; text-transform: uppercase; letter-spacing: 0.1em;
  font-family: inherit; font-size: 0.9rem; transition: border-color .15s, text-shadow .15s, background .15s;
}
.gb-submit:hover { border-color: var(--fg); text-shadow: var(--glow-strong); background: rgba(0, 255, 65, 0.05); }
.gb-submit:disabled { opacity: 0.5; cursor: default; }
.gb-msg { color: var(--fg-soft); font-size: 0.9rem; min-height: 1em; }
.gb-msg.err { color: var(--err); text-shadow: none; }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }
.gb-list-head {
  margin-top: 2.6rem; border-bottom: 1px solid var(--fg); padding-bottom: 0.4rem;
  color: var(--fg); text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.9rem;
  display: flex; justify-content: space-between;
}
.gb-entry { border-bottom: 1px solid var(--border); padding: 1rem 0; }
.gb-entry-head { display: flex; gap: 0.6rem; align-items: baseline; flex-wrap: wrap; }
.gb-name { color: var(--fg); font-size: 1.1rem; }
.gb-contact { color: var(--fg-soft); font-size: 0.85rem; }
.gb-time { margin-left: auto; color: var(--muted); font-size: 0.8rem; }
.gb-body { color: var(--fg-soft); margin-top: 0.35rem; white-space: pre-wrap; line-height: 1.5; word-break: break-word; }
.gb-empty { color: var(--muted); padding: 1rem 0; }

/* ── 10. App (iframe) view ───────────────────────────────────────────────────
   When a hosted app is loaded in-shell, the column breaks out to full width and
   the iframe fills the viewport below a slim chrome bar. */
body.is-app { padding: 0; }
body.is-app .shell {
  max-width: none; gap: 0;
  min-height: 100vh; height: 100vh;
}
body.is-app header.shell-header {
  padding: 0.5rem clamp(1rem, 5vw, 2rem);
  gap: 1rem; align-items: center;
}
body.is-app .mark { font-size: 1.3rem; }
body.is-app footer.shell-footer { display: none; }
/* Let the iframe fill the viewport: #view → #view-content → iframe must all be
   flex columns that grow (min-height:0 lets them shrink below content size). */
body.is-app #view { min-height: 0; }
body.is-app #view-content { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; width: 100%; }
.app-frame { flex: 1 1 auto; width: 100%; min-height: 0; border: 0; display: block; background: var(--bg); }
.app-back {
  display: none;
  color: var(--fg-soft); font-size: 0.95rem; text-transform: uppercase; letter-spacing: 0.08em;
  border-bottom: 1px solid transparent; transition: color .15s, border-color .15s, text-shadow .15s;
}
.app-back:hover { color: var(--fg); border-bottom-color: var(--fg); text-shadow: var(--glow-strong); }
body.is-app .app-back { display: inline; }
body.is-app nav.shell-nav { display: none; }
/* (The standalone "sawwave bar" .sw-embed-bar lives in sawwave-tokens.css — it's
   an app-facing component, not used by the homepage shell.) */

/* ── 11. Responsive ──────────────────────────────────────────────────────── */
@media (max-width: 540px) {
  .project { grid-template-columns: 2rem 1fr auto; gap: 0.9rem; padding: 1rem 0; }
  .project-split { grid-template-columns: 2rem 2fr 1fr; gap: 0.9rem; padding: 1rem 0; }
  .project-main { gap: 0.9rem; }
}
