/* ═══════════════════════════════════════════════════════════════════════════
   INFO — the landing page's 41/59 split: portrait alone in the left column,
   everything else in the right.

   The right column follows the tabular structure of the Hélio Teles reference:
   a label column, then values, with tight leading and rows packed close rather
   than spaced out over screens of air.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The portrait column's share of the page, as on the landing page. Unitless so
   the bar title can offset by it too — a percentage can't be used as a
   multiplier, and the title has to start exactly where the text column does. */
:root{ --info-split: .41; }

.info{
  margin-top: var(--bar);
  /* named, because the sticky aside rests on --info-top and the corner block
     sits on --info-bottom; each has to track the padding across breakpoints */
  --info-top: 40px;
  /* The sections and the corner block both bottom-align against this, so it is
     the page's bottom margin and moving it moves them together. The site's 10px
     gutter plus 50: on the gutter alone the last line crowded the screen edge.
     .info__main's min-height derives from it too, which is what lets the group
     travel rather than the page merely ending sooner. */
  --info-bottom: calc(var(--gut) + 50px);
  padding: var(--info-top) var(--gut) var(--info-bottom);
  position: relative;
  display: flex;
  align-items: flex-start;
  font-size: calc(var(--fs) + 1px);   /* the info page reads a step up from the
                                    site's 13px */
  --lh-info: 1.28;               /* leading a touch airier than base */
  line-height: var(--lh-info);
}

/* ─── Left: portrait only ─────────────────────────────────────────────────── */

.info__aside{
  width: calc(var(--info-split) * 100%);
  flex: 0 0 calc(var(--info-split) * 100%);
  /* pinned while the right column scrolls past. The offset equals .info's
     padding-top, so it sits at rest exactly where it will stick — no jump on
     the first scroll. */
  position: sticky;
  top: calc(var(--bar) + var(--info-top));
}

.portrait{
  margin: 0;
  width: 200px;
  background: var(--media-ground);
}
.portrait img{ width: 100%; height: auto; display: block; }

/* ─── Reach out ───────────────────────────────────────────────────────────────
   Sits under the portrait in the left column. Contact lines then location, as
   the reference sets its enquiries block. */

.reach{ margin-top: 2.4em; }

/* The line the page's own description ends on, standing where the "Reach out"
   label did. Full ink rather than the quiet grey the section labels wear: it's
   a sentence, not a label. */
.reach__intro{
  margin: 0;
  color: var(--ink);
  text-wrap: pretty;
}

.reach__list{
  margin: .5em 0 0;
  padding: 0;
  list-style: none;
}
/* medium, so the two links carry more weight than the prose around them */
.reach__list a{
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: from-font;
}
.reach__list a:hover{ color: var(--ink-quiet); }

/* ─── Currently listening to ───────────────────────────────────────────────────
   The page's bottom left corner: the label with the waveform beside it on one
   line, the title on a ticker below, the whole lockup ranged left.

   The waveform is CSS keyframes, not a JS interval repainting inline heights:
   five staggered animations cost nothing, need no timer to clean up, and stop
   dead for anyone who asked for less motion. It runs whatever the state — it is
   the component's ornament, not a playback light. What is actually live is
   carried by the label ("Currently listening to" against "On repeat") and by
   the ink: full for a track playing now, quiet for a standing pick. */

.np{
  /* two lines, ranged left: the label and waveform share the first, the ticker
     has the second */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  width: 140px;

  /* a step down from the column: the site's own 13px against the page's 14px */
  font-size: var(--fs);

  /* Bottom left, raised so the first line sits on the "I love" row.

     Measured from the same --info-bottom the sections bottom-align to, so the
     two move together. 21.5em of this lockup's own 13px is the distance from the
     group's bottom up to that row: the three lists between are type-relative,
     as are the 1.6em margins between them, and the one svh margin in the stack
     sits above "I love" rather than below it — so the offset holds at any window
     height. It is still a measured offset: adding or removing a list item moves
     that row and this would need measuring again. */
  position: absolute;
  bottom: calc(var(--info-bottom) + 21.5em);
  left: var(--gut);
}
/* an author display beats the UA sheet's [hidden]{display:none}, so without
   this the component still reserves its space while hidden */
.np[hidden]{ display: none; }

/* Full ink, so it reads as the lockup's title against the quiet ticker under
   it. --ink rather than a literal white: it has to be near-black in the light
   theme and near-white in the dark one.

   Neither this nor the ticker sets a size any more: three steps up from the 11px
   they were is exactly the column's own 14px, so they inherit it and track the
   page if that ever changes. */
.np__what{
  display: block;
  line-height: 1.3;
  color: var(--ink);
}

/* the label and the waveform, side by side */
.np__top{
  display: flex;
  align-items: center;
  gap: .6em;
}

.np__bars{
  display: flex;
  align-items: center;
  gap: 2px;
  height: 12px;                 /* the tallest a bar gets, so it reserves its room */
}
.np__bars i{
  width: 1px;
  background: var(--ink);
  border-radius: 1px;
  animation: np-bar .6s ease-in-out infinite alternate;
}
/* quiet while it isn't actually playing, so liveness still reads in the ink */
.np:not(.is-playing) .np__bars i{ background: var(--ink-quiet); }

/* Staggered around the .6s base, so the five read as a waveform and not one
   blinking block. The delays are negative: each bar starts already partway
   through its cycle, so the group is mid-waveform on the very first frame
   instead of rising from flat together. */
.np__bars i:nth-child(1){ animation-delay: -.08s; animation-duration: .52s; }
.np__bars i:nth-child(2){ animation-delay: -.30s; animation-duration: .66s; }
.np__bars i:nth-child(3){ animation-delay: -.17s; animation-duration: .46s; }
.np__bars i:nth-child(4){ animation-delay: -.40s; animation-duration: .60s; }
.np__bars i:nth-child(5){ animation-delay: -.23s; animation-duration: .72s; }

@keyframes np-bar{
  from{ height: 2px; }
  to  { height: 12px; }
}

/* ─── The title's ticker ───────────────────────────────────────────────────────
   Subtle, and moving, so a long name costs one narrow line instead of wrapping
   or ellipsing. Two copies chase each other: the reel is exactly twice one
   copy's width, so translating half of it lands the second copy precisely where
   the first began and the seam never shows. The trailing space is padding on
   each copy rather than a gap between them — a gap would fall outside the
   halves and the loop would jump by that much. */

.np__ticker{
  display: block;
  width: 100%;                  /* the clip needs a width of its own to scroll in */
  overflow: hidden;
  margin-top: 7px;
  color: var(--ink-quiet);
  /* the ends fade rather than cutting the letters off square */
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

.np__reel{
  display: inline-flex;
  white-space: nowrap;
  animation: np-ticker 14s linear infinite;
}
.np__title{ padding-right: 2.5em; }

@keyframes np-ticker{
  from{ transform: translateX(0); }
  to  { transform: translateX(-50%); }
}

.np:hover .np__what{ color: var(--ink-quiet); }
.np:hover .np__ticker{ color: var(--ink); }

@media (prefers-reduced-motion: reduce){
  /* held at a middle height, and the title sits still rather than scrolling */
  .np__bars i{ animation: none; height: 7px; }
  .np__reel{ animation: none; }
}

/* ─── Now: place and local time ────────────────────────────────────────────────
   Back under the contact lines, in the left column's flow. */

.now{ margin-top: 1.2em; }

.now__where{ margin: 0; color: var(--ink-quiet); }

/* tabular figures so the seconds tick without the line shifting width */
.now__clock{
  margin: .3em 0 0;
  color: var(--ink-quiet);
  font-variant-numeric: tabular-nums;
}

/* ─── Right: everything ───────────────────────────────────────────────────── */

.info__main{
  width: calc((1 - var(--info-split)) * 100%);
  flex: 0 0 calc((1 - var(--info-split)) * 100%);

  /* The sections bottom-align in the column: on a window taller than the page
     needs, the slack collects above Previously rather than below the last list,
     so the group sits on the content's bottom edge, level with the corner
     block. The folder keeps its place at the top.

     A column, so one auto margin can absorb that slack; the min-height is what
     creates it. When the content is taller than the window there is no slack,
     the auto margin resolves to nothing and the layout is untouched. */
  display: flex;
  flex-direction: column;
  min-height: calc(100svh - var(--bar) - var(--info-top) - var(--info-bottom));
}
.info__main > .sect:first-of-type{ margin-top: auto; }

/* label column, then values — the reference's shape */
.sect{
  display: grid;
  grid-template-columns: 30% 1fr;
  gap: 0 var(--gut);
  margin-bottom: 5svh;
}
.sect:last-child{ margin-bottom: 0; }

/* the four lists read as one group, so they sit closer together */
.sect--tight{ margin-bottom: 1.6em; }

/* the list drops clear of its label's line instead of racing it */
.sect--tight .sect__body{ margin-top: 2px; }

/* The experience table needs the whole column: her role titles run to 46
   characters against the reference's "Design Director", and at 17px four
   columns inside 571px forced them onto three lines. Label goes above instead
   of beside, and every row lands on one line. */
.sect--wide{ grid-template-columns: 1fr; }
.sect--wide .sect__head{ margin-bottom: .7em; }

.sect__head{
  margin: 0;
  font-size: inherit;
  font-weight: 400;
}
.sect__head .pill{ margin-top: -5px; }   /* pill cap aligns to the prose top */

/* the supporting labels are quieter than the five pills */
.sect__label{ color: var(--ink-quiet); }

.sect__body > p{ margin: 0 0 .8em; }
.sect__body > p:last-child{ margin-bottom: 0; }
.sect__body{ max-width: 56ch; text-wrap: pretty; }
.sect--wide .sect__body{ max-width: none; }

.list{ margin: 0; padding: 0; list-style: none; }

.mark{ color: var(--ink-quiet); }

/* ─── Experience ──────────────────────────────────────────────────────────────
   Four columns: company, role, city, dates. The company leads the row in
   medium, since it's what the eye scans for.

   No column takes the slack, and justify-content packs the four of them left as
   one lockup — otherwise the city and dates hang off the far edge of a 743px
   column. The role still shrinks and wraps before anything else does, since her
   titles run to 46 characters against the reference's "Design Director". */

.xp{
  display: grid;
  grid-template-columns:
    max-content minmax(0, max-content) max-content max-content;
  justify-content: start;
  gap: .35em calc(var(--gut) * 2.4);
  align-items: baseline;
  /* the table sets tighter than the prose: every cell is one line, so it needs
     no room for a line to breathe against the one below it */
  line-height: calc(var(--lh-info) * 1em - 2px);
}
.xp__co{ font-weight: 500; }
.xp__when{ color: var(--ink-quiet); white-space: nowrap; }
.xp span{ min-width: 0; }

/* ─── Bar title ───────────────────────────────────────────────────────────────
   chrome.css puts .bar__title a third across, where a project page's image
   column starts. This page's columns divide elsewhere, so the title moves to
   the left edge of the text column — the same relationship, measured off the
   same split the columns use. 100% here is the bar's own width, which is why
   the gutter is added back the way .info's padding adds it. */

.bar__title{
  left: calc(var(--gut) + (100% - var(--gut) * 2) * var(--info-split));
  /* chrome.css clamps the title against --nav-reserve measured from its own
     third. Starting further across, this page has correspondingly less room, so
     the clamp is restated from this start point — otherwise it over-grants the
     gap to the nav by the difference between a third and the split. */
  max-width: calc(100% - var(--nav-reserve) - var(--gut)
                  - (100% - var(--gut) * 2) * var(--info-split));
}

/* ─── W tabs ──────────────────────────────────────────────────────────────────
   The five W answers as folder tabs, so they cost one panel's height instead of
   five stacked sections.

   Outline only, as asked, and the tab's sides turn out into the panel's top line
   on a curve, the way a folder tab does.

   That join has to be built, not decorated. The row reserves an 11px band below
   the tabs, so an active tab's side borders stop short of the panel line rather
   than running down to it; a quarter-circle arc either side then carries each
   side the rest of the way, starting tangent to the tab's border and ending
   tangent to the panel's. An earlier version drew the arcs beside a full-height
   tab, which left the straight side *and* the curve both showing — a line with
   a hook off it. The arcs are sized so their own borders land exactly on the
   two strokes they join, since a pixel of error here reads as a step. */

/* The folder is narrower than the column it sits in: the panel is the measure
   plus its own 18px of padding either side, so the answers fill it edge to edge
   instead of running the full 743px and leaving the right half empty. */
.wtabs{
  margin-bottom: 5svh;
  max-width: calc(52ch + 36px);
}

.wtabs__row{
  display: flex;
  gap: 0;
  /* the flare band: 11px for the arcs, then -1px to drop the band's bottom edge
     onto the panel's 1px top border so the arcs land on it and not beside it */
  padding-bottom: 11px;
  margin-bottom: -1px;
}

.wtab{
  appearance: none;
  -webkit-appearance: none;
  background: none;
  font: inherit;
  /* the five Ws are the loudest thing on the page — three steps up from the
     prose, in medium. 1em resolves against .wtabs, so it tracks the page size */
  font-size: calc(1em + 3px);
  font-weight: 500;
  color: var(--ink-quiet);
  cursor: pointer;

  position: relative;
  flex: 0 0 auto;
  /* short on the bottom by roughly the flare band, so the label sits centred in
     the whole folder-tab shape rather than high in it */
  padding: 12px 18px 1px;
  border: 1px solid transparent;
  border-bottom: 0;
  border-radius: 10px 10px 0 0;
  transition: color .18s var(--ease-out-quart);
}
.wtab:hover{ color: var(--ink); }

.wtab[aria-selected="true"]{
  color: var(--ink);
  border-color: var(--ink);
  background: var(--ground);
  z-index: 1;
  /* The tab's own box now stops above the panel line, so its ground has to be
     carried down over the panel's top border to open the folder's mouth. The
     offset equals the band, which lands the fill's bottom edge on that border
     and no lower — a pixel more would nick the panel's left border under the
     first tab.

     Three copies, not one: the mouth has to reach 10px past each side of the
     tab, out to where the arcs land, or the panel's border stays visible
     running straight on underneath each curve. Offsets rather than a spread,
     because a spread would round the fill's bottom corners and pull it back off
     the very points the arcs meet. */
  box-shadow:
      0   11px 0 var(--ground),
    -10px 11px 0 var(--ground),
     10px 11px 0 var(--ground);
}

/* The arcs. Each is an 11px box sitting in the band: its bottom border lands on
   the panel's top border and its side border on the tab's side border, and the
   radius is the box's full size so only the curve draws and never a straight
   stub. The result is one continuous stroke from tab to panel.

   The offsets are -11px, not -10px, because an absolutely positioned box is
   placed against its ancestor's *padding* box — a pixel inside the tab's own
   1px border. Getting that wrong puts every arc a pixel off the border it is
   supposed to continue, which on a 1px stroke reads as a broken edge. */
.wtab[aria-selected="true"]::before,
.wtab[aria-selected="true"]::after{
  content: "";
  position: absolute;
  bottom: -11px;                /* no correction: the tab has no bottom border */
  width: 11px;
  height: 11px;
  border-bottom: 1px solid var(--ink);
}
.wtab[aria-selected="true"]::before{
  left: -11px;                  /* right border lands on the tab's left border */
  border-right: 1px solid var(--ink);
  border-bottom-right-radius: 11px;
}
.wtab[aria-selected="true"]::after{
  right: -11px;                 /* left border lands on the tab's right border */
  border-left: 1px solid var(--ink);
  border-bottom-left-radius: 11px;
}

/* The first tab is flush with the panel's left edge, so there's no line out
   there to flare into — its left border just runs on down to meet the panel's,
   and the panel squares that corner to receive it. */
.wtab:first-child[aria-selected="true"]::before{
  left: -1px;                   /* onto the tab's left border, as above */
  width: 1px;
  border: 0;
  border-radius: 0;
  background: var(--ink);
}

.wtabs__panel{
  border: 1px solid var(--ink);
  border-radius: 10px;
  padding: 22px 18px;
  /* Fixed, not a minimum: the five answers run from two lines to seven, and a
     panel that sized to its content shunted everything below it up and down on
     every tab. 12.8em clears the tallest (What) at this measure. */
  height: 12.8em;
}

/* the corner the first tab's left border runs down into */
.wtabs:has(.wtab:first-child[aria-selected="true"]) .wtabs__panel{
  border-top-left-radius: 0;
}

.wpanel{ display: none; }
.wpanel.is-on{
  display: block;
  animation: wpanel-in .26s var(--ease-out-quart) both;
}
/* no measure of its own — the panel's width is the measure */
.wpanel p{ margin: 0 0 .8em; text-wrap: pretty; }
.wpanel p:last-child{ margin-bottom: 0; }

@keyframes wpanel-in{ from { opacity: 0; transform: translateY(6px) } }

@media (prefers-reduced-motion: reduce){
  .wpanel.is-on{ animation: none; }
}

@media (max-width: 639px){
  .wtab{ padding: 12px 11px 1px; }
  /* Below the folder's own width the answers wrap deeper, so the panel needs
     more height — still fixed, so tabbing doesn't shunt the page here either.
     Sized for a 320px screen, the narrowest the answers have to survive. */
  .wtabs__panel{ padding: 18px 14px; height: 16.1em; }
}

/* Five tabs at 17px medium don't fit a 320px screen on 11px of padding — the
   row doesn't wrap, so When would hang off the edge. The gaps give way before
   the type does. */
@media (max-width: 360px){
  .wtab{ padding: 12px 8px 1px; }
}

/* ─── Narrow ──────────────────────────────────────────────────────────────── */

@media (max-width: 1023px){
  /* one column from here down, so there is no text edge to align to */
  .bar__title{ display: none; }

  /* one column here, and nothing bottom-aligns, so the page keeps a proper
     run-out at the end instead of stopping 10px short */
  .info{ display: block; --info-top: 18svh; --info-bottom: 12svh; }

  .info__aside,
  .info__main{ width: auto; }
  /* one column here, so there is nothing to bottom-align against — the sections
     follow the portrait and the links straight down the page */
  .info__main{ min-height: 0; }
  .info__main > .sect:first-of-type{ margin-top: 0; }
  .info__aside{ position: static; }

  .portrait{ width: 150px; }
  .reach{ margin-top: 1.6em; margin-bottom: 8svh; }

  .sect{ grid-template-columns: 1fr; margin-bottom: 4svh; }
  .sect__head{ margin-bottom: 10px; }
  .sect--tight{ margin-bottom: 1.6em; }
  .sect__body{ max-width: none; }

  /* four columns will not survive a phone — one row becomes one block */
  .xp{ grid-template-columns: 1fr; gap: 0; }
  .xp span{ display: block; }
  /* the company starts each row, so it carries the gap between them */
  .xp__co{ margin-top: 1em; }
  .xp__co:first-child{ margin-top: 0; }
}
