/* ============================================================
   SILICA WORKS - the thread, the FAQ as a conversation
   ------------------------------------------------------------
   Not an accordion. Every question and every answer is already
   in the markup, in reading order, and stays readable with the
   script absent. js/thread.js only changes how each one arrives:
   a small bevelled chip with three dots pulsing in it drops into
   the thread, holds for a beat of scroll, then widens, opens and
   sets its copy down a line at a time. Scrolling is what sends
   the messages, so the reader is the one holding the
   conversation.

   THE SHEET IS INK, AND IT IS LAID OVER THE PAGE. The team above
   is on paper; the thread is a dark sheet with the brand's
   chamfer cut from its two top corners (.pg-sheet), so the paper
   shows in the cuts the way the reel shows in the cuts of "Who we
   are" on the home page. The bubbles are the two grounds the site
   already has - paper for the reader, the accent for the studio -
   so on ink they need no hairline at all to hold their edge.

   THE BUBBLE IS THE BRAND'S BEVEL WITH ONE CORNER LEFT SQUARE.
   Three corners take the nav's 14px cut; the fourth, the lower
   corner on the speaker's side, stays square and becomes the
   tail. That is how a chat bubble says who is talking without a
   drawn tail, and it is still the one corner the whole site uses.
   The inset is absolute, so the cut is identical on the 56px
   seed and the 700px card - the shape never deforms mid-tween.

   THE RAIL IS THE OTHER END OF THE LINE. On the left, pinned for
   the length of the thread: who you are talking to (the two
   faces from the cards above), what they are doing right now
   ("Ahsan is typing" while an answer is still a seed), how far
   through the conversation you are, and every question as a
   jump link.
   ============================================================ */

.th {
  --sheet-ground: var(--sw-ink);
  --th-notch: 14px;
  --th-muted: #8f8f88;
  --th-rule: #2e2e2a;

  display: block;
  min-height: 0;
  color: var(--sw-paper);
  /* The home page's sheet rhythm (the spacing tokens in
     css/styles.css): its sheet top over the title, and a foot that
     holds the nav once it parks at the end of the page (js/menu.js,
     data-nav-park) - the bar's offset, the bar, and the same air
     above it home.html gives .sw-showcase. */
  padding: var(--sw-sheet-top) 2.5rem calc(var(--sw-park-off) + var(--sw-bar-h, 60px) + var(--sw-park-air));
  /* Pulled up under the team so the chamfer cuts into it rather
     than into a strip of empty paper. */
  margin-top: calc(var(--pg-chamfer) * -1);
}

.th .sw-section-title,
.th .sw-ft-word {
  color: var(--sw-paper);
}

.th ::selection {
  background: var(--sw-accent);
  color: var(--sw-ink);
}

.th-head {
  display: block;
}

.th-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: 1.5rem;
  align-items: start;
  /* Title to content, as on the home page's sheets. */
  margin-top: var(--sw-title-gap);
}

/* ------------------------------------------------------------
   The rail
   ------------------------------------------------------------ */
.th-rail {
  grid-column: 1 / span 4;
  position: sticky;
  top: clamp(1.5rem, 6vh, 4rem);
  display: flex;
  flex-direction: column;
  gap: 1.75rem;
}

/* The contact card: the nav panel's surface (--sw-bg-soft and its
   border), bevelled like every plate. */
.th-card {
  --n: var(--th-notch);
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 1rem;
  align-items: center;
  padding: 1.1rem 1.25rem;
}

/* Edge and face, the face's cut pulled in so the edge is one
   thickness round the bevels - see --pg-bevel-k in css/pages.css. */
.th-card::before,
.th-card::after {
  content: "";
  position: absolute;
  z-index: -1;
  clip-path: polygon(var(--c) 0, calc(100% - var(--c)) 0, 100% var(--c),
    100% calc(100% - var(--c)), calc(100% - var(--c)) 100%,
    var(--c) 100%, 0 calc(100% - var(--c)), 0 var(--c));
}

.th-card::before { --c: var(--n); inset: 0; background: #3a3a36; }
.th-card::after { --c: calc(var(--n) - 1.5px * var(--pg-bevel-k)); inset: 1.5px; background: #1b1b1b; }

.th-faces {
  display: flex;
}

.th-face {
  --n: 7px;
  width: 2.75rem;
  height: 2.75rem;
  overflow: hidden;
  background: #2a2a26;
  clip-path: polygon(var(--n) 0, calc(100% - var(--n)) 0, 100% var(--n),
    100% calc(100% - var(--n)), calc(100% - var(--n)) 100%,
    var(--n) 100%, 0 calc(100% - var(--n)), 0 var(--n));
  transition: transform 0.5s var(--pg-ease);
}

.th-face + .th-face {
  margin-left: -0.6rem;
}

.th-face img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: grayscale(1) contrast(1.1);
}

/* Whoever is typing steps forward. */
.th-face.is-typing {
  transform: translateY(-0.3rem);
}

.th-card-who {
  display: grid;
  gap: 0.35rem;
  min-width: 0;
}

.th-card-who b {
  font-family: "sohne-bold", "Inter Tight", sans-serif;
  font-weight: 800;
  font-size: 0.95rem;
  letter-spacing: -0.01em;
  text-transform: uppercase;
}

.th-card-names {
  font: 500 0.72rem/1.2 "Inter Tight", sans-serif;
  color: var(--th-muted);
}

/* The status line. Two states in one slot: the resting line with
   the live dot, and "<name> is typing" with the three dots. Both
   are in the markup and the script swaps which one shows, so the
   line never reflows. */
.th-status {
  position: relative;
  height: 1rem;
  overflow: hidden;
  font: 600 0.64rem/1rem "Inter Tight", sans-serif;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.th-status > span {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  white-space: nowrap;
  transition: transform 0.45s var(--pg-ease), opacity 0.3s ease;
}

.th-status-rest { color: var(--th-muted); }
.th-status-typing { color: var(--sw-accent); transform: translateY(100%); opacity: 0; }

.th-status.is-typing .th-status-rest { transform: translateY(-100%); opacity: 0; }
.th-status.is-typing .th-status-typing { transform: translateY(0); opacity: 1; }

.th-status .pg-live {
  width: 0.42rem;
  height: 0.42rem;
  box-shadow: none;
}

.th-status-typing i {
  display: inline-block;
  width: 0.26rem;
  height: 0.26rem;
  margin-right: -0.3rem;
  border-radius: 50%;
  background: currentColor;
  animation: th-dot 1.1s ease-in-out infinite;
}

.th-status-typing i:nth-child(2) { animation-delay: 0.15s; }
.th-status-typing i:nth-child(3) { animation-delay: 0.3s; margin-right: 0.1rem; }

/* How far through. The numerals are the rail's one loud element,
   so the reader can see the conversation advancing at a glance. */
.th-progress {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: end;
  gap: 0.75rem 1rem;
}

.th-progress-label {
  font: 500 0.68rem/1 "Inter Tight", sans-serif;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--th-muted);
}

/* In the hand the team roles and the FAQ lede use. The face has
   one weight, so the stroke thickens it enough to hold its place as
   the rail's loud element. */
.th-count {
  font-family: "Shadows Into Light", "Inter Tight", cursive;
  font-weight: 400;
  font-size: clamp(2.2rem, 3.2vw, 3.4rem);
  line-height: 0.8;
  letter-spacing: 0.02em;
  -webkit-text-stroke: 0.03em currentColor;
  font-variant-numeric: tabular-nums;
}

.th-count small {
  font-size: 0.4em;
  letter-spacing: 0;
  color: var(--th-muted);
}

.th-bar {
  grid-column: 1 / -1;
  height: 2px;
  background: var(--th-rule);
  overflow: hidden;
}

.th-bar > span {
  display: block;
  height: 100%;
  background: var(--sw-accent);
  transform-origin: 0 50%;
  transform: scaleX(var(--th-p, 1));
  transition: transform 0.7s var(--pg-ease);
}

/* The index: every question, as a way to jump to it. */
.th-index {
  list-style: none;
  counter-reset: th;
  border-top: 1px solid var(--th-rule);
}

.th-index li {
  border-bottom: 1px solid var(--th-rule);
}

.th-index button {
  all: unset;
  box-sizing: border-box;
  width: 100%;
  display: grid;
  grid-template-columns: 2.2rem 1fr auto;
  align-items: center;
  gap: 0.5rem;
  padding: 0.62rem 0;
  font: 500 0.82rem/1.3 "Inter Tight", sans-serif;
  color: var(--th-muted);
  cursor: pointer;
  transition: color 0.3s ease;
}

.th-index button:hover,
.th-index button:focus-visible {
  color: var(--sw-paper);
}

.th-index button:focus-visible {
  outline: 1.5px solid var(--sw-accent);
  outline-offset: 2px;
}

.th-index-num {
  font-variant-numeric: tabular-nums;
  font-size: 0.68rem;
  letter-spacing: 0.08em;
}

/* The tick that lands as the answer does - a small bevelled chip
   in the accent, empty until then. */
.th-index-tick {
  --n: 3px;
  width: 0.65rem;
  height: 0.65rem;
  background: var(--th-rule);
  clip-path: polygon(var(--n) 0, 100% 0, 100% calc(100% - var(--n)),
    calc(100% - var(--n)) 100%, 0 100%, 0 var(--n));
  transition: background-color 0.4s ease, transform 0.5s var(--pg-ease);
}

.th-index li.is-answered button { color: var(--sw-paper); }
.th-index li.is-answered .th-index-tick { background: var(--sw-accent); }
.th-index li.is-current .th-index-tick { transform: scale(1.35) rotate(45deg); }

.th-note {
  max-width: 17rem;
  color: #c9c9c2;
  transform: rotate(-3deg);
  transform-origin: 0 0;
}

.th-note .pg-arrow {
  left: 100%;
  top: 30%;
  margin-left: 0.5rem;
}

/* ------------------------------------------------------------
   The thread
   ------------------------------------------------------------ */
.th-thread {
  grid-column: 6 / -1;
  display: flex;
  flex-direction: column;
  gap: clamp(3rem, 7vh, 5rem);
}

/* A question and its answer are one exchange, so they sit closer
   to each other than to the next pair - that difference is what
   groups them, with no rule or box doing it. */
.th-item {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  /* A jump from the rail lands with the question a third of the
     way down the screen rather than hard against the top. */
  scroll-margin-top: 30vh;
}

/* Each row is the lane a card sits in, and it is the row - not the
   card - that holds the height open. The script pins a min-height
   here from the measured card, so the seed collapsing never lets
   the thread below it jump up. */
.th-row {
  display: flex;
  align-items: flex-end;
  gap: 0.75rem;
}

.th-row--q { justify-content: flex-end; }
.th-row--a { justify-content: flex-start; }

/* The speaker's face, sitting at the foot of the answer, against
   the bubble's square corner. */
.th-avatar {
  --n: 6px;
  flex: none;
  width: 2.5rem;
  height: 2.5rem;
  /* Lifted clear of the attribution under the lane, so the face
     sits level with the bubble's foot rather than the label's. */
  margin-bottom: 1.6rem;
  overflow: hidden;
  background: #2a2a26;
  clip-path: polygon(var(--n) 0, calc(100% - var(--n)) 0, 100% var(--n),
    100% calc(100% - var(--n)), calc(100% - var(--n)) 100%,
    var(--n) 100%, 0 calc(100% - var(--n)), 0 var(--n));
}

.th-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: grayscale(1) contrast(1.1);
}

.th-lane {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 0;
  max-width: 84%;
}

.th-row--q .th-lane {
  align-items: flex-end;
  /* A question is one line and wants to stay one; an answer is a
     paragraph and wants a measure. So the question lane is let out
     to the whole thread and the answer keeps the 84%. */
  max-width: 100%;
}

/* ------------------------------------------------------------
   The card
   ------------------------------------------------------------
   One box is both the seed and the finished message. The script
   tweens width, height and padding between the two, so nothing
   here may put a transition on those - GSAP writes them inline
   every frame and a CSS transition on top would drag every step.
   ------------------------------------------------------------ */
.th-msg {
  --n: var(--th-notch);
  position: relative;
  max-width: 100%;
  /* Clips the copy while the box is still a seed, or the lines
     spill out over the dots for the frames before the expand. */
  overflow: hidden;
  padding: 1.35rem 1.6rem 1.4rem;
  color: var(--sw-ink);
  will-change: width, height, transform;
}

/* The reader: paper, tail on the lower right. */
.th-msg--q {
  background: var(--sw-paper);
  transform-origin: 100% 100%;
  clip-path: polygon(var(--n) 0, calc(100% - var(--n)) 0, 100% var(--n),
    100% 100%, var(--n) 100%, 0 calc(100% - var(--n)), 0 var(--n));
}

/* The studio: the accent, tail on the lower left, toward the face. */
.th-msg--a {
  background: var(--sw-accent);
  transform-origin: 0 100%;
  clip-path: polygon(var(--n) 0, calc(100% - var(--n)) 0, 100% var(--n),
    100% calc(100% - var(--n)), calc(100% - var(--n)) 100%,
    0 100%, 0 var(--n));
}

.th-content {
  /* Shrink-wraps, so the measured width is the width the words
     want rather than the whole lane. */
  width: max-content;
  max-width: 100%;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

/* THE MEASURE SITS ON THE PARAGRAPHS, IN REM, AND NOWHERE ELSE.
   ------------------------------------------------------------
   It was min(46ch, 100%) on .th-content, and every answer opened
   to the full width of its lane with the copy wrapping at 46ch
   inside it - a card 250px wider than its words.

   The card is sized by its content (it is a shrink-wrapped flex
   item), and while a box is being sized that way a percentage
   inside it has nothing to resolve against. The browser treats
   the whole min() as "none" for that pass, so the card asked for
   the paragraphs' full one-line width and the lane capped it.

   A max-width with no percentage in it is honoured in that pass,
   so it goes on each paragraph. The rem keeps it one figure
   across the heavier first line and the lighter detail, where a
   ch would differ with each line's own face and size. The 100%
   on .th-content stays, and still stops a phone lane overflowing:
   in the real layout pass it has a card to resolve against. */
.th-msg--a .th-content > * {
  max-width: 29rem;
}

.th-content p {
  font-family: "sohne-normal", "Inter Tight", sans-serif;
  font-size: 1rem;
  line-height: 1.6;
  letter-spacing: -0.003em;
  /* Ink at reduced alpha rather than a grey: on the accent a mid
     grey lands on the green's own luminance and goes to mud. */
  color: rgba(20, 20, 15, 0.74);
}

/* The question is the line the reader scans, and the first line of
   an answer is the answer - the lines after it are the reasoning.
   Both are set as a small heading so the thread can be taken at a
   glance and left wherever the reader likes.

   The question is a real <h3>, so a screen reader can walk the
   thread question by question the way the eye does. */
.th-msg--q .th-content h3,
.th-msg--a .th-content p:first-child {
  font-weight: 500;
  font-family: "sohne-medium", "Inter Tight", sans-serif;
  font-size: clamp(1.05rem, 0.4vw + 0.8rem, 1.25rem);
  line-height: 1.38;
  letter-spacing: -0.015em;
  color: var(--sw-ink);
}

/* A link inside an answer: ink, underlined, the arrow riding out. */
.th-content a {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  width: max-content;
  font: 700 0.72rem/1 "Inter Tight", sans-serif;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--sw-ink);
  padding-bottom: 0.3rem;
  border-bottom: 1.5px solid var(--sw-ink);
}

.th-content a span {
  transition: transform 0.45s var(--pg-ease);
}

.th-content a:hover span,
.th-content a:focus-visible span {
  transform: translateX(0.3em);
}

/* The attribution sits outside the card, under its lane, so the
   card holds nothing but the message. */
.th-who {
  display: block;
  margin-top: 0.6rem;
  padding-inline: 0.2rem;
  font: 600 0.62rem/1 "Inter Tight", sans-serif;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--th-muted);
}

.th-who b {
  color: var(--sw-paper);
  font-weight: 600;
}

/* ------------------------------------------------------------
   The dots
   ------------------------------------------------------------
   Centred by inset, so they sit in the middle of the seed at
   whatever size the box currently is, and they are gone before it
   finishes widening. Shown only while the script is driving -
   three pulsing dots inside an already readable message mean
   nothing.
   ------------------------------------------------------------ */
.th-dots {
  position: absolute;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  pointer-events: none;
}

.th-dots span {
  width: 0.375rem;
  height: 0.375rem;
  /* The one round thing inside a bubble, and on purpose: a dot is a
     mark, not a surface, and bevelling one this small only blurs it
     into a square. */
  border-radius: 50%;
  background: var(--sw-ink);
  animation: th-dot 1.1s ease-in-out infinite;
}

.th-dots span:nth-child(2) { animation-delay: 0.15s; }
.th-dots span:nth-child(3) { animation-delay: 0.3s; }

/* The dots only pulse while their seed is actually typing - dropped
   in and not yet opened (.th-typing, set by js/thread.js). Left
   running, every message's three dots pulsed for the whole visit:
   behind the open copy, and down the page where Chrome does not
   paint them and so cannot hand the animation to the compositor -
   it runs them on the main thread instead, restyling some fifty
   elements on every frame. Same for the rail's dots, which only
   show while somebody is typing. */
.th-dots span,
.th-status-typing i {
  animation-play-state: paused;
}

.th-msg.th-typing .th-dots span,
.th-status.is-typing .th-status-typing i {
  animation-play-state: running;
}

@keyframes th-dot {
  0%, 100% { transform: translateY(0); opacity: 0.4; }
  50% { transform: translateY(-0.2rem); opacity: 1; }
}

/* Held back only once the script has the section. */
.th[data-thread-ready] .th-dots { display: flex; }
.th[data-thread-ready] .th-content > * { opacity: 0; }
.th[data-thread-ready] .th-who,
.th[data-thread-ready] .th-avatar { opacity: 0; }

/* ------------------------------------------------------------
   The end of the thread - your turn
   ------------------------------------------------------------
   The conversation closes on the studio's side with an open
   question, and then hands the reader the composer. It is a real
   form: what is typed here is carried into the brief on the
   contact page (?q=), so asking the last question is also the
   first step of starting a project.
   ------------------------------------------------------------ */
.th-end {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.th-compose {
  --n: 12px;
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.55rem 0.55rem 0.55rem 1.4rem;
}

.th-compose::before,
.th-compose::after {
  content: "";
  position: absolute;
  z-index: -1;
  clip-path: polygon(var(--c) 0, 100% 0, 100% calc(100% - var(--c)),
    calc(100% - var(--c)) 100%, 0 100%, 0 var(--c));
  transition: background-color 0.25s ease;
}

.th-compose::before { --c: var(--n); inset: 0; background: #3a3a36; }
.th-compose::after { --c: calc(var(--n) - 1.5px * var(--pg-bevel-k)); inset: 1.5px; background: #1b1b1b; }

/* The edge lights when the field has focus - the ring a clipped box
   cannot draw with an outline. */
.th-compose:focus-within::before {
  background: var(--sw-accent);
}

.th-compose input {
  flex: 1;
  min-width: 0;
  padding: 0.9rem 0;
  border: 0;
  outline: 0;
  background: transparent;
  font-family: "sohne-normal", "Inter Tight", sans-serif;
  font-size: clamp(1rem, 0.5vw + 0.8rem, 1.3rem);
  color: var(--sw-paper);
  caret-color: var(--sw-accent);
}

.th-compose input::placeholder {
  color: #6f6f68;
}

/* The site's button on the composer's dark face: a paper edge, the
   composer's own face inside it, filled with the accent on hover. */
.th-compose .pg-btn {
  --btn-edge: var(--sw-paper);
  --btn-face: #1b1b1b;
  --btn-text: var(--sw-paper);
  flex: none;
}

.th-compose-note {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  font: 500 0.66rem/1.4 "Inter Tight", sans-serif;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--th-muted);
}

.th-compose-note a {
  color: var(--sw-paper);
  text-decoration: none;
  border-bottom: 1px solid currentColor;
}

/* ------------------------------------------------------------
   Narrow
   ------------------------------------------------------------
   The rail stops being a rail: no room beside the thread for it,
   so the contact card and the count sit above the thread as a
   header and the index and note drop out. The bubbles take nearly
   the whole measure and the colour does the telling.
   ------------------------------------------------------------ */
@media (max-width: 1000px) {
  .th {
    --th-notch: 11px;
    padding-left: 1.25rem;
    padding-right: 1.25rem;
    padding-top: var(--sw-sheet-top);
  }

  /* minmax(0, 1fr), not 1fr. A bare 1fr has an auto minimum, and the
     bubbles' copy is width: max-content - so the track took the
     widest paragraph's one-line width as its floor and ran the
     whole thread 100px off the side of a phone. */
  .th-grid {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 3rem;
  }

  .th-rail,
  .th-thread {
    grid-column: 1;
  }

  .th-rail {
    position: relative;
    top: 0;
    gap: 1.25rem;
  }

  .th-index,
  .th-note {
    display: none;
  }

  .th-lane {
    max-width: calc(100% - 3.25rem);
  }

  .th-msg {
    padding: 1.1rem 1.2rem;
  }

  .th-avatar {
    width: 2.25rem;
    height: 2.25rem;
  }

  .th-compose {
    padding-left: 1rem;
  }

  .th-compose .pg-btn-label {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .th-dots span,
  .th-status-typing i {
    animation: none;
  }

  .th-status > span,
  .th-bar > span,
  .th-face,
  .th-index-tick {
    transition: none;
  }
}
