/* Consulting page: the capability cards.
   Achromatic at rest per the design system's Colorless Chrome rule; Stratos
   Blue arrives only on hover, as the signal that the card is a live target —
   the same role it plays on the founder portraits. */

.offerings {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.5rem;
}
@media (min-width: 640px) {
  .offerings { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .offerings { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.offer {
  --offer-accent: #1e4fb8; /* Stratos Blue, shifted to clear AA on paper */
  display: flex;
  flex-direction: column;
  padding: 1.5rem;
  border: 1px solid var(--color-border);
  border-radius: 0.75rem;
  background: var(--color-card);
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
}
.dark .offer {
  --offer-accent: #7aa5f5; /* 5.6:1 on Carbon Black */
}
.offer:hover {
  border-color: color-mix(in oklab, var(--offer-accent) 45%, var(--color-border));
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.06);
}

.offer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

/* Controls tier: 8px radius, matching the icon tiles in the product chrome. */
.offer__tile {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  flex: none;
  border: 1px solid var(--color-border);
  border-radius: 0.5rem;
  background: color-mix(in oklab, var(--color-foreground) 4%, transparent);
  color: var(--color-muted-foreground);
  transition: color 0.25s ease, border-color 0.25s ease, background-color 0.25s ease;
}
.offer:hover .offer__tile {
  color: var(--offer-accent);
  border-color: color-mix(in oklab, var(--offer-accent) 35%, var(--color-border));
  background: color-mix(in oklab, var(--offer-accent) 9%, transparent);
}
.offer__tile svg {
  width: 1.125rem;
  height: 1.125rem;
}

/* The running index across the four cards, which is also the count the heading
   names. Decorative ordering only, so it is hidden from assistive tech. */
.offer__num {
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: color-mix(in oklab, var(--color-muted-foreground) 60%, transparent);
  transition: color 0.25s ease;
}
.offer:hover .offer__num {
  color: var(--color-muted-foreground);
}

.offer__title {
  margin-top: 1.25rem;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.4;
  text-wrap: balance;
  color: var(--color-foreground);
}

/* Four cards, so two columns rather than three: at three the fourth card would
   sit alone on its own row, and each column would be narrow enough to break the
   longer capability lines twice. Two holds them to one or two lines each. */
.offerings--pairs {
  gap: 1.25rem;
}
@media (min-width: 768px) {
  .offerings--pairs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .offerings--pairs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* The capabilities inside a card. Hairlines rather than bullets: these are a
   short spec list, not prose, and the rules line up with how the rest of the
   site separates items in a group. */
.offer__list {
  margin-top: 1.125rem;
  padding: 0;
  list-style: none;
}
.offer__list li {
  padding: 0.6875rem 0;
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--color-muted-foreground);
}
.offer__list li + li {
  border-top: 1px solid var(--color-border);
}
.offer__list li:last-child {
  padding-bottom: 0;
}

@media (prefers-reduced-motion: reduce) {
  .offer,
  .offer__tile,
  .offer__num { transition-duration: 0.01ms; }
}

/* ---------- how an engagement runs ---------- */
/* A timeline rather than four panels: the rail carries the order, so the steps
   no longer need a card and hairlines to say they belong to one sequence.

   Every state below is expressed as a data-state on the item. The stylesheet's
   own defaults are the *finished* state -- filled nodes, filled rail -- so a
   visitor without JS gets a complete, sensible timeline instead of four grey
   placeholders waiting for an animation that will never run. site.js sets
   data-timeline-anim on the list, which is what switches these rules into
   their pending state before stepping them forward. */
.timeline {
  --tl-node: 1.75rem;
  --tl-gap: 1.25rem;
  list-style: none;
  /* Owns its own top gap: a Tailwind mt-* in the markup loses to this rule,
     which is later in the cascade at the same specificity. */
  margin: 3.5rem 0 0;
  padding: 0;
}

.tl {
  position: relative;
  display: grid;
  grid-template-columns: var(--tl-node) minmax(0, 1fr);
  gap: var(--tl-gap);
  padding-bottom: 2.25rem;
}
.tl:last-child { padding-bottom: 0; }

/* The connector spans the gap between two nodes, drawn on the item above so
   the last step has no tail hanging off it. The next node starts exactly at
   this item's bottom edge, so the offsets are pure clearance: enough to keep
   the line off both circles, and off the halo an active one carries. */
.tl:not(:last-child)::before,
.tl:not(:last-child)::after {
  content: "";
  position: absolute;
  top: calc(var(--tl-node) + 0.5rem);
  bottom: 0.5rem;
  left: calc(var(--tl-node) / 2);
  width: 2px;
  transform: translateX(-1px);
  border-radius: 1px;
}
.tl:not(:last-child)::before { background: var(--color-border); }
/* ::after is the travelling fill; it sits on top of the track and grows down. */
.tl:not(:last-child)::after {
  background: var(--color-primary);
  transform: translateX(-1px) scaleY(1);
  transform-origin: top;
}

.tl__node {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--tl-node);
  height: var(--tl-node);
  border: 2px solid var(--color-primary);
  border-radius: 999px;
  background: var(--color-primary);
  color: var(--color-primary-foreground);
}
/* The dot a pending node shows in place of the check. */
.tl__node::before {
  content: "";
  position: absolute;
  width: 0.4375rem;
  height: 0.4375rem;
  border-radius: 999px;
  background: var(--color-muted-foreground);
  opacity: 0;
}

.tl__check {
  width: 0.875rem;
  height: 0.875rem;
}

.tl__meta {
  font-size: 0.6875rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-muted-foreground);
}

.tl__title {
  margin-top: 0.375rem;
  font-size: 1.0625rem;
  font-weight: 600;
  text-wrap: balance;
  color: var(--color-foreground);
}

.tl__body {
  margin-top: 0.375rem;
  /* The section runs the full 7xl container; the measure is held, not the box. */
  max-width: 44rem;
  line-height: 1.65;
  color: var(--color-muted-foreground);
}
/* The node sits on the meta line's baseline area, so the text column starts at
   the same optical height as the circle rather than above it. */
@media (min-width: 640px) {
  .timeline { --tl-node: 2rem; --tl-gap: 1.5rem; }
  .tl { padding-bottom: 2.75rem; }
  .tl__check { width: 1rem; height: 1rem; }
}

/* ---------- and the same timeline, mid-animation ---------- */
/* Present only once JS has taken the list over. */
[data-timeline-anim] .tl__node {
  border-color: var(--color-border);
  background: transparent;
  transition: background 0.5s ease, border-color 0.5s ease, box-shadow 0.5s ease;
}
[data-timeline-anim] .tl__node::before {
  opacity: 1;
  transition: opacity 0.3s ease, background 0.5s ease;
}
[data-timeline-anim] .tl__check {
  opacity: 0;
  transform: scale(0.4);
  transition: opacity 0.35s ease, transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
}
[data-timeline-anim] .tl:not(:last-child)::after {
  transform: translateX(-1px) scaleY(0);
  transition: transform 0.85s cubic-bezier(0.65, 0, 0.35, 1);
}
/* Copy starts dimmed so the eye follows the node that is currently lit. */
[data-timeline-anim] .tl__main {
  opacity: 0.45;
  transition: opacity 0.6s ease;
}

/* The step being walked through: ring picks up the foreground and a halo opens
   around it. The halo is a box-shadow that arrives with the step rather than a
   keyframed pulse -- the loop already supplies the repetition, and a second
   rhythm running underneath it would fight the first. */
[data-timeline-anim] .tl[data-state="active"] .tl__node {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 5px color-mix(in oklab, var(--color-primary) 12%, transparent);
}
[data-timeline-anim] .tl[data-state="active"] .tl__node::before {
  background: var(--color-primary);
}
[data-timeline-anim] .tl[data-state="active"] .tl__main,
[data-timeline-anim] .tl[data-state="done"] .tl__main {
  opacity: 1;
}

[data-timeline-anim] .tl[data-state="done"] .tl__node {
  border-color: var(--color-primary);
  background: var(--color-primary);
  box-shadow: none;
}
[data-timeline-anim] .tl[data-state="done"] .tl__node::before { opacity: 0; }
[data-timeline-anim] .tl[data-state="done"] .tl__check {
  opacity: 1;
  transform: scale(1);
}
[data-timeline-anim] .tl[data-state="done"]:not(:last-child)::after {
  transform: translateX(-1px) scaleY(1);
}

/* Someone who has asked for less motion still gets the finished timeline; the
   JS skips the walk and marks every step done at once, and these rules take the
   transitions off so nothing slides into place. */
@media (prefers-reduced-motion: reduce) {
  [data-timeline-anim] .tl__node,
  [data-timeline-anim] .tl__node::before,
  [data-timeline-anim] .tl__check,
  [data-timeline-anim] .tl__main,
  [data-timeline-anim] .tl:not(:last-child)::after {
    transition: none;
  }
}
