/*
  Surface, button and badge system.

  The two-tier structure below came from adblockplus.org/whats-new; the
  LOOK now comes from the "Portfolio landing page" mockups (turn 7a light /
  7b dark), which is where the coral glow vocabulary, the 26px radius and
  the three-part elevation recipe are from. What did not change is the
  mechanism: every variant still only sets custom properties, and there is
  still one `background-image` declaration in this file.

  ---- The two tiers ----

  `.card` is the OUTER tier: it sits on the wave artwork, carries a shadow,
  and blurs what is behind it. `.panel` is the INNER tier: it only ever sits
  inside a card, has no shadow and no blur, and separates itself by hue.
  Nesting a card in a card gives you two shadows fighting and two backdrop
  blurs stacked, which is what `.panel` exists to avoid.

  ---- The fill recipe ----

  Every surface stacks up to three background layers, outermost first, copied
  straight from the reference -- `.whats-new-pricing` really is a radial over
  a linear over the page:

      1. --lit-wash    a radial "light from above", only on .card--lit
      2. --tint-*      a coloured gradient, only on .card--cool / --warm
      3. --*-fill-*    the base translucent fill, always

  Layering rather than swapping is what lets a tint compose with either tier
  without restating the base fill, and it is why every modifier below only
  sets custom properties: there is ONE `background-image` declaration in this
  file and every variant goes through it.

  ---- What is deliberately not here ----

  No literal colours. Every value comes from a token that css/variables.css
  defines twice, so the system follows the theme toggle -- and so
  `node tools/check-contrast.js` can read the alphas back out and prove each
  surface still clears WCAG AA over the whole wave palette.
*/

/* ---- The shared recipe ---- */
.card,
.panel {
  position: relative;
  color: var(--card-text);
  border: 1px solid var(--surface-line);
  background-image:
    var(--lit, none),
    linear-gradient(var(--tint-top, transparent), var(--tint-bottom, transparent)),
    linear-gradient(var(--surface-top), var(--surface-bottom));
}

.card > :last-child,
.panel > :last-child {
  margin-bottom: 0;
}

/* Body copy follows the SURFACE's muted tone, not the page's -- base.css
   points `p` at --text-secondary, which js/waves.js drives to suit the
   artwork behind the section. A card has its own fill on top of that. */
.card p,
.panel p {
  color: var(--card-text-muted);
}

/* Tier 1. The blur is what makes a genuinely translucent fill legible over
   six moving wave colours: it softens the artwork underneath instead of
   letting a hard wave edge run through the middle of a paragraph. */
.card {
  --surface-top: var(--card-fill-top);
  --surface-bottom: var(--card-fill-bottom);
  --surface-line: var(--card-border);

  padding: var(--space-4);
  border-radius: var(--card-radius);
  box-shadow: var(--card-shadow);
  backdrop-filter: blur(14px);
}

/* Tier 2. No shadow, no blur, smaller radius -- it is already inside
   something that has all three. */
.panel {
  --surface-top: var(--panel-fill-top);
  --surface-bottom: var(--panel-fill-bottom);
  --surface-line: var(--panel-border);

  padding: var(--space-3);
  border-radius: var(--panel-radius);
}


/* ---- Tints ----
   Written for both tiers, so a tinted thing can stand on the artwork or be
   nested without needing a second class. Cool carries figures and warm
   carries voice, which is the reference's own pairing: blue stat tiles
   against amber testimonials. The point is that grouping reads by HUE, so no
   extra shadow or border weight is needed to say what belongs with what. */
.card--cool,
.panel--cool {
  --tint-top: var(--tint-cool-top);
  --tint-bottom: var(--tint-cool-bottom);
  --surface-line: var(--tint-cool-line);
}

.card--warm,
.panel--warm {
  --tint-top: var(--tint-warm-top);
  --tint-bottom: var(--tint-warm-bottom);
  --surface-line: var(--tint-warm-line);
}

/* The featured surface: a coral wash raked in from the top-left corner,
   plus a little extra of the same glow in the shadow stack. The wash is a
   background LAYER rather than a ::before, so it composites with whatever
   fill and tint the card already has. */
.card--lit {
  --lit: var(--lit-wash);
  --surface-line: var(--tint-warm-line);

  box-shadow: var(--card-shadow), var(--lit-shadow);
}

/* The same light from the bottom-right corner. For a card that closes a
   composition -- the Skills screen's Activities & Honours tile, last in the
   grid -- where the top-left wash would pull the eye back to the start. */
.card--lit-end {
  --lit: var(--lit-wash-end);
}

/* A soft circle of coral bleeding in from OFF the card's top-right corner.
   The mockup's credentials card lit it from the top-left, but on the phase
   and case cards that put the glow right behind the heading and first lines
   of copy; the right-hand corner is usually open space, so the light reads
   as ambience instead of a stain under the text. It has to be a real
   positioned element rather than another background layer because it is
   anchored outside the box it lights, which a background-position cannot
   express. */
.card--glow {
  overflow: hidden;
}

.card--glow::before {
  content: "";
  position: absolute;
  top: -5.625rem;
  right: -3.75rem;
  width: 21.25rem;
  height: 21.25rem;
  border-radius: 50%;
  background: var(--lit-glow);
  pointer-events: none;
}

/* Everything the glow sits behind needs a stacking context of its own, or
   the circle paints over the numerals. */
.card--glow > * {
  position: relative;
}

/* The mockup's hover: a 4px lift on a long, soft curve. Opt-in rather than
   applied to every `.card`, because a card the visitor cannot click should
   not move under the pointer. In practice that means the case-study
   screenshots, which open the lightbox, and nothing else. */
.lift {
  transition: transform 350ms cubic-bezier(0.2, 0.7, 0.3, 1),
              box-shadow 350ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.lift:hover {
  transform: translateY(-4px);
}

@media (prefers-reduced-motion: reduce) {
  .lift {
    transition: none;
  }

  .lift:hover {
    transform: none;
  }
}

/* A surface holding nothing but an image. The padding becomes a visible mat,
   which is what gives the reference's media panels their framed look, so the
   image needs its own radius to sit inside the container's. */
.card--media {
  padding: var(--space-2);
  overflow: hidden;
}

.card--media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: calc(var(--card-radius) - var(--space-2));
}


/* ---- Badge ----
   Their `whats-new-trial-badge`: a small opaque pill above a heading. Opaque
   is the point -- it is the one label on this page that sits over raw wave
   artwork and stays legible, because its own fill hides whatever colour
   happens to be behind it. */
.badge {
  display: inline-block;
  margin: 0 0 clamp(var(--space-2), 1.4vh, var(--space-3));
  padding: var(--space-1) var(--space-3);
  background: var(--accent);
  color: var(--text-on-accent);
  border-radius: 999px;
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* Small caps-y label above a heading, as on the reference's section openers.
   Uses the CARD's accent, not the page's: --text-accent is calibrated for an
   opaque page background instead. */
.eyebrow {
  margin: 0 0 var(--space-2);
  color: var(--card-text-accent);
  font-size: var(--fs-small);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}


/* ---- Credential rows ----
   The three credential figures, as the mockup sets them: ONE card, three
   rows, a right-aligned numeral column and a label column, hairline
   dividers between.

   This replaced a row of three separate stat tiles, and the reason is
   measure. Three tiles across a text column that is a fraction of the
   viewport minus a rail minus a portrait left each label about 190px, and
   "Years leading web-based software engineering and Agile/Scrum
   development" set to five ragged lines in it. As a row the same label gets
   the whole column and sets to one or two. The numeral keeps a column of
   its own so all three figures align on their right edge and all three
   labels start at the same x -- which is what makes the card scan as a
   table of credentials rather than as three unrelated facts.

   A `dl` rather than a `ul`: each row really is a term and its definition,
   and it gets the numeral/label pairing across to a screen reader for free.

   The grid is declared on the `dl` with both cells placed into it, not on a
   per-row wrapper. Wrapping each row would let each one size its own
   numeral column and the figures would stop aligning. */
.creds {
  display: grid;
  /* Sized so the widest figure clears the cell's own padding at BOTH ends of
     the clamp -- at the first attempt it cleared it at the top end only and
     the leading digit was sliced off by the card's left edge on a laptop.
     Two digits at the largest step run about 1.2x the type size; the column
     also has to hold the word YEARS under them, which is what actually sets
     the minimum. */
  grid-template-columns: clamp(6rem, 14cqw, 9.5rem) minmax(0, 1fr);
  margin: 0;
}

.creds__value,
.creds__label {
  padding-block: clamp(var(--space-2), 1.5vh, 1.375rem);
  border-bottom: 1px solid var(--card-divider);
}

/* Last row carries no rule -- the card's own edge already ends the list. */
.creds > :nth-last-child(-n + 2) {
  border-bottom: 0;
}

.creds__value {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  padding-left: var(--space-4);
  padding-right: var(--space-3);
  min-width: 0;
}

/* `line-height: 1.1` is a floor, not a preference: tighter than that and
   the descender of a numeral gets clipped in some rendering paths. */
.creds__num {
  font-family: var(--font-display);
  /* Sized in cqw -- against the TEXT COLUMN, not the viewport. The column
     is the viewport minus a rail minus a portrait, so the same 1024px
     laptop leaves 380px here where a 1600px screen leaves 700px; type set
     off vw is a size too big in the first case and the section is locked to
     one screen, so the overflow is not recoverable by scrolling. */
  font-size: clamp(1.75rem, 10cqw, 3.25rem);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.04em;
  color: var(--card-text);
}

.creds__unit {
  font-size: clamp(0.6875rem, 2.6cqw, 1rem);
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--card-text-accent);
}

/* `balance` rather than `pretty`: at this measure the failure mode is a
   one-word last line, not a bad break. */
.creds__label {
  display: flex;
  align-items: center;
  margin: 0;
  padding-right: var(--space-4);
  font-size: clamp(0.9375rem, 4.2cqw, 1.4375rem);
  line-height: 1.4;
  text-wrap: balance;
  color: var(--card-text);
}


/* Narrow columns: the numeral stops earning a column of its own and sits
   above its label instead.

   A CONTAINER query, not a media query -- whether the split works depends
   on the width of the text column, which on this page is the viewport
   minus a rail minus a portrait, so the same 1280px viewport gives 598px
   here and a 1655px one gives 861px. A viewport breakpoint cannot see
   that. */
@container textcol (max-width: 22rem) {
  .creds {
    grid-template-columns: minmax(0, 1fr);
  }

  .creds__value {
    align-items: flex-start;
    padding: var(--space-2) var(--space-3) 0;
    border-bottom: 0;
  }

  .creds__value + .creds__label {
    padding: 0 var(--space-3) var(--space-2);
  }
}


/* ---- Quote ----
   Deliberately a different KIND of object from the credentials card, and
   the difference has to survive the theme swap, so it is carried by the
   coral wash raked across its top-left corner (`.card--lit`).

   The mockup also runs a 4px coral rule down the left edge, inset from the
   card's top and bottom and fading out as it falls. It came out reading as a
   fragment of a border that stops for no reason rather than as a mark, so it
   is not here. The wash carries the differentiation on its own.

   Its fill is OPAQUE where every other surface here is translucent. That is
   not decoration: the waves move behind this page, and a translucent quote
   card let the artwork change the effective background under the longest
   run of text on the screen. Opaque, its contrast is a fixed number.

   The oversized decorative quote mark is gone. Both marks are now part of
   the sentence, at the same size and colour as the rest of it -- the mockup
   asks for the quotation to look like a quotation rather than like a piece
   of punctuation someone enlarged. */
.quote {
  display: flex;
  align-items: center;
  padding: clamp(var(--space-3), 2.4vh, 2rem) clamp(var(--space-4), 2.2vw, 2.5rem);
  overflow: hidden;
  background-color: var(--surface-quote);
}

.quote__text {
  margin: 0;
  font-size: clamp(0.9375rem, 4.2cqw, 1.4375rem);
  line-height: 1.5;
  text-wrap: pretty;
  color: var(--card-text);
}


/* ---- Buttons ---- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: clamp(0.9rem, 0.7rem + 0.6vw, 1.4375rem) clamp(1.5rem, 1.1rem + 1.4vw, 2.75rem);
  font-family: var(--font-body);
  font-size: clamp(0.875rem, 0.8rem + 0.35vw, 1.3125rem);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-decoration: none;
  border: 1px solid transparent;
  border-radius: 999px;
  cursor: pointer;
  transition: background-color 350ms cubic-bezier(0.2, 0.7, 0.3, 1),
              box-shadow 350ms cubic-bezier(0.2, 0.7, 0.3, 1),
              transform 350ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

/* Nothing about a button's own paint may outlive the pointer. Without this
   the pill keeps its hover elevation for the length of the transition while
   the transform has already started coming down, which is the other half of
   what read as "two things happen when I stop hovering". */
.btn:not(:hover) {
  transition-delay: 0s;
}

/* The same 4px lift the cards use, so the page has one hover gesture
   rather than a 1px one here and a 4px one there. */
.btn:hover {
  transform: translateY(-4px);
}

.btn:active {
  transform: translateY(0);
}

.btn--primary {
  background: var(--btn-bg);
  border-color: var(--btn-border);
  color: var(--btn-text);
  box-shadow: var(--btn-shadow);
}

.btn--primary:hover {
  background: var(--btn-bg-hover);
  box-shadow: var(--btn-shadow-hover);
}

.btn--primary:active {
  box-shadow: var(--btn-shadow);
}

/* The quieter half of the pair, and a frosted pill rather than a solid one:
   a mostly-transparent fill with a crisp hairline holding its edge. It gets
   no coral glow at all -- that glow is the only thing marking the primary
   button as primary, since both are the same size and shape.

   The border is 1.5px, not the shared 1px. On a translucent fill the edge is
   most of what draws the shape, and at 1px it disappeared into the wave
   behind it at the top of the pill, where the two values are closest.

   Its hover shadow is its OWN, and that is a fix rather than a tidy-up. It
   used to borrow the primary's -- `var(--btn-shadow-hover), var(--btn2-shadow)`
   -- which is three layers including an inset one against a resting state of
   one layer that is not inset. Two shadow stacks of different shapes cannot
   be interpolated, so the browser swapped them discretely: the lift animated
   over 350ms and the shadow appeared and vanished in one frame, which read
   as two separate things happening on the way out. Resting and hover are now
   the same single layer, and the whole state transitions together. */
.btn--secondary {
  background: var(--btn2-bg);
  border: 1.5px solid var(--btn2-border);
  color: var(--btn2-text);
  box-shadow: var(--btn2-shadow);
}

.btn--secondary:hover {
  box-shadow: var(--btn2-shadow-hover);
}

/* The coral pill: an action that stands INSIDE a card. The primary button's
   plum was chosen to stand on the waves, and on the dark theme's plum card
   it is the same colour as the card. Coral is the one glow colour the system
   already has, so this is that glow as a fill. Near-black label, 7:1. Used
   by the lock screen's Unlock. */
.btn--accent {
  background: var(--btn-accent-bg);
  color: var(--btn-accent-text);
  box-shadow: var(--btn-accent-shadow);
}

.btn--accent:hover {
  background: var(--btn-accent-bg-hover);
}

.btn--accent:disabled {
  cursor: progress;
  opacity: 0.7;
  transform: none;
}

/* ...and its quiet partner inside a card: no fill, a coral hairline, card
   ink. Both colours are the card's own tokens, so no new pair to check. */
.btn--outline {
  background: transparent;
  border: 1.5px solid var(--card-text-accent);
  color: var(--card-text);
}

.btn--outline:hover {
  background: var(--card-divider);
}

@media (prefers-reduced-motion: reduce) {
  .btn {
    transition: none;
  }

  .btn:hover,
  .btn:active {
    transform: none;
  }
}
