/* sections.css: reveal system, work log, services, about, contact */

/* ---------------------------------------------------------------- reveal */
/* Base state is FULLY VISIBLE. Hiding only ever happens inside the
   @supports block below, so unsupported browsers never lose content. */
.reveal {
  opacity: 1;
  transform: none;
}

@supports (animation-timeline: view()) {
  @keyframes reveal-up {
    from { opacity: 0; transform: translateY(var(--travel)); }
    to   { opacity: 1; transform: none; }
  }

  .reveal {
    animation: reveal-up linear both;
    animation-duration: auto;
    animation-timeline: view();
    animation-range: entry 10% entry 55%;
  }

  .reveal-late {
    animation-range: entry 25% entry 70%;
  }
}

/* ------------------------------------------------------------ work / log */
.log-feed {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--color-border-2);
  border-radius: 8px;
  background: var(--color-surface-1);
  overflow: hidden;
}

.log-line {
  display: grid;
  grid-template-columns: 5.5rem 1fr;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  transition: background-color var(--dur-state) var(--ease-standard);
}

.log-line:last-child {
  border-bottom: 0;
}

.log-line:hover {
  background: var(--color-surface-2);
}

/* level text labels carry the meaning: colour is redundant reinforcement */
.log-level {
  font-weight: 640;
  letter-spacing: 0.04em;
}

.log-level.is-ok { color: var(--color-ok); }

/* cap the measure: without this the explanation runs the whole row, about 119
   characters at desktop widths, which is far past a comfortable reading line */
.log-msg {
  color: var(--color-text-2);
  max-width: 76ch;
}

.log-msg strong {
  display: block;
  color: var(--color-text-hi);
  font-weight: 560;
  margin-bottom: 2px;
}

@media (max-width: 600px) {
  .log-line {
    grid-template-columns: 1fr;
    gap: var(--space-1);
  }
}

/* Track record and How I can help are proof and offer, so the boundary between them
   is deliberately tighter than the others: work closes two fluid steps down and
   services opens one step down, giving about 136 px at 1440 where every other
   section boundary is about 237 px */
#work {
  padding-bottom: var(--space-fluid-md);
}

#services {
  padding-top: var(--space-fluid-lg);
}

/* -------------------------------------------------------------- services */
.services-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
}

.service-card {
  background: var(--color-surface-1);
  border: 1px solid var(--color-border-2);
  border-radius: 8px;
  padding: var(--space-6);
  transition:
    border-color var(--dur-state) var(--ease-standard),
    transform var(--dur-state) var(--ease-spring),
    box-shadow var(--dur-state) var(--ease-out);
}

.service-card:hover {
  transform: translateY(-2px);
  border-color: var(--color-accent-dim);
  box-shadow: 0 0 0 1px rgba(97, 168, 255, .15), 0 12px 32px rgba(0, 0, 0, .4);
}

.service-name {
  font-family: var(--font-mono);
  font-size: var(--step-1);
  color: var(--color-text-hi);
  font-weight: 640;
}

.service-name .cmd-verb { color: var(--color-accent); }

.service-desc {
  color: var(--color-text-2);
  margin-top: var(--space-3);
}

.service-list {
  list-style: none;
  padding: 0;
  margin-top: var(--space-4);
}

.service-list li {
  font-family: var(--font-mono);
  font-size: var(--step-0);
  color: var(--color-text-2);
  padding-block: 4px;
  /* hanging indent: wrapped lines align with the text, not under the marker */
  padding-inline-start: 1.4em;
  text-indent: -1.4em;
  /* a two-line bullet splits into two similar lines instead of a line plus a tail word */
  text-wrap: balance;
}

.service-list li::before {
  content: '> ';
  color: var(--color-secondary);
}

@media (max-width: 900px) {
  .services-grid {
    grid-template-columns: 1fr;
  }
}

/* ----------------------------------------------------------------- about */
/* One column by default, with the portrait first in source order so a narrow viewport
   shows the face above the prose. From 1000px the grid opens into two columns with the
   portrait in the first, against the left edge of the section, and the prose beside it.
   The About terminal stays below both, outside this grid. */
.about-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-fluid-md);
}

/* Portrait on the left, prose on the right. The prose stops at its 66ch measure and the
   portrait is 216px wide (sized to finish level with the three paragraphs, see portrait.css),
   so together they do not fill the container. Leading with the portrait keeps that slack on
   the outside: the prose starts right after the window and its ragged edge trails into the
   margin the way any column of text does, and the portrait shares its left edge with the
   heading above it and the terminal below it.
   The portrait track is the window's own width, so the prose track takes everything else
   and the 66ch cap does the rest. 1000px is the breakpoint because below it the prose
   track beside a portrait would fall under 40 characters, which is too narrow a measure
   to read. */
@media (min-width: 1000px) {
  .about-grid {
    grid-template-columns: 216px minmax(0, 1fr);
    align-items: start;
  }

  .about-portrait {
    grid-column: 1;
    grid-row: 1;
  }

  .about-text {
    grid-column: 2;
    grid-row: 1;
  }
}

.about-text p {
  max-width: 66ch;
  margin-bottom: var(--space-4);
  color: var(--color-text);
}

/* ---------------------------------------------------- about terminal */
/* A second terminal that types a paraphrase of the About copy when it scrolls into view.
   The window and transcript classes come from hero.css, which is loaded on every page. */
.about-term {
  margin-top: var(--space-fluid-md);
}

.term-window-static .term-body {
  display: grid;
  min-height: 0;
  max-height: none;
  overflow: visible;
  cursor: default;
}

/* the live log and the hidden finished copy share one cell, so the body is always the
   finished height and nothing below About moves while the transcript types */
.term-window-static .term-body > .term-log {
  grid-area: 1 / 1;
  min-width: 0;
}

.term-ghost {
  visibility: hidden;
  pointer-events: none;
}

.about-term .term-log p {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* hanging indent: the middot starts at 2ch and a wrapped continuation lands at 4ch
   instead of running back to the left edge */
.about-term .t-indent {
  padding-inline-start: 4ch;
  text-indent: -2ch;
}

/* --------------------------------------------------------------- contact */
.contact-box {
  background: var(--color-surface-1);
  border: 1px solid var(--color-border-2);
  border-radius: 12px;
  padding: var(--space-fluid-md);
  position: relative;
  overflow: hidden;
}

.contact-box::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(90deg, var(--color-accent), var(--color-secondary));
}

.contact-lead {
  font-size: var(--step-1);
  color: var(--color-text-2);
  max-width: 48ch;
}

.contact-lead + .contact-lead {
  margin-block-start: var(--space-3);
}

/* the email and the Copy email button read as one path, so they share a row */
.contact-email-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-block: var(--space-4);
}

.contact-email {
  font-family: var(--font-mono);
  font-size: var(--step-3);
  color: var(--color-accent);
  display: inline-block;
  margin-block: 0;
  text-decoration: none;
}

.contact-email:hover,
.contact-email:focus-visible {
  color: var(--color-accent-bright);
  text-decoration: underline;
}

.copy-email {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  min-height: 44px;
  padding: 10px 20px;
  border-radius: 6px;
  border: 1px solid var(--color-border-strong);
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
  transition:
    border-color var(--dur-state) var(--ease-standard),
    color var(--dur-state) var(--ease-standard);
}

.copy-email:hover {
  border-color: var(--color-accent);
  color: var(--color-accent-bright);
}

.copy-email[data-copied] {
  border-color: var(--color-ok);
  color: var(--color-ok);
}

.contact-meta {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--color-text-3);
  margin-top: var(--space-4);
}

@media (max-width: 600px) {
  .contact-email {
    font-size: var(--step-2);
  }
}

.contact-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-2);
}

/* ------------------------------------------------------------ phone layout */
/* The rules below apply only at narrow widths and short viewport heights. */

@media (max-width: 640px) {
  .contact-actions .btn,
  .contact-email-row .copy-email {
    width: 100%;
  }

  /* stacked full-width on phones, so the copy button carries the same label size as
     the buttons under it; the 14 px chip is a desktop-only choice */
  .contact-email-row .copy-email {
    font-size: var(--step-0);
  }

  /* the email is the primary tap target in this block; give it a real touch height */
  .contact-email {
    padding-block: 6px;
  }
}

@media (max-width: 400px) {
  .contact-email {
    font-size: var(--step-1);
  }

  .contact-box {
    padding: 1.25rem;
  }
}

/* on a short viewport the card's own trailing padding is the last thing between the
   button and the end of the scroll, so trim it for a little extra margin */
@media (max-height: 520px) {
  .contact-box {
    padding-bottom: var(--space-3);
  }
}

@media (max-width: 360px) {
  .service-card {
    padding: var(--space-4);
  }

  .log-line {
    padding: var(--space-3);
  }
}
