/* ==========================================================================
   Components
   ========================================================================== */

/* ---- Buttons ------------------------------------------------------------ */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 48px;
  padding: var(--space-3) var(--space-8);
  font-family: var(--l99-font-signage);
  font-size: var(--text-base);
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  border-radius: var(--radius-block);
  transition: background-color var(--l99-dur-micro) var(--l99-ease-standard),
              color var(--l99-dur-micro) var(--l99-ease-standard),
              transform var(--l99-dur-micro) var(--l99-ease-standard),
              /* forms.js drops opacity to 0.5 while a form submits; without
                 this it snapped. */
              opacity var(--l99-dur-micro) var(--l99-ease-standard);
}

/* White on trail, 4.84:1 -- the spec's "buttons & labels" pairing. It used to
   be dark text on a mint sheet, which made the primary action the lightest
   thing on the page. */
.btn--primary {
  background: var(--trail);
  color: var(--white);
}
.btn--primary:hover {
  background: var(--forest);
  color: var(--white);
  transform: translateY(-2px);
}
.btn--primary:active { transform: translateY(0); }

.btn--quiet {
  padding-inline: 0;
  color: var(--ink);
  background: none;
  border-bottom: 2px solid var(--trail);
  border-radius: 0;
}
.btn--quiet:hover {
  color: var(--trail);
  border-bottom-color: var(--forest);
}
.btn--quiet:active { transform: translateY(1px); }
/* On the forest block the hairline vanished entirely, so this was patched
   inline on exactly one of six instances. Now it is a rule. */
.section--brand .btn--quiet { color: var(--cream); border-bottom-color: var(--mint); }
.section--brand .btn--quiet:hover { color: var(--mint); border-bottom-color: var(--white); }

.btn__icon {
  width: 1.1em;
  height: 1.1em;
  flex: none;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.75;
}

/* ---- Site header -------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  /* Deep forest, per the brand spec. This waited on a reversed lockup: the old
     mark's black "MY"/"TRAVELS" measured 1.39:1 here. */
  background: var(--forest);
  color: var(--cream);
  --l99-ring: var(--mint);
  border-bottom: var(--rule-hair) solid color-mix(in srgb, var(--cream) 20%, var(--forest));
  transition: box-shadow var(--l99-dur-transition) var(--l99-ease-standard);
}
/* Condenses once you leave the top of the page. */
.site-header[data-stuck] {
  border-bottom-color: color-mix(in srgb, var(--cream) 34%, var(--forest));
  background: color-mix(in srgb, var(--forest) 92%, transparent);
  backdrop-filter: blur(10px);
}

.site-header__inner {
  max-width: var(--container);
  margin-inline: auto;
  padding: var(--space-4) var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  /* The header compacts on scroll. Padding is a layout property, and this is a
     sticky element that repaints while scrolling, so animating it is the
     expensive way to do this -- the logo below used to make it worse by
     animating `width` at the same time.

     The logo is now a transform, which the compositor handles alone. The
     padding is NOT animated: the height change lands in one step at the scroll
     threshold instead of thrashing layout for 300ms. What the eye follows is
     the logo scaling and the shadow arriving, and both of those are still
     smooth. Do not put a transition back on this property. */
}
.site-header[data-stuck] .site-header__inner { padding-block: var(--space-2); }

.site-header__logo { display: block; flex: none; }
.site-header__logo img {
  /* 168, not 148. The lockup changed shape when it went back to the globe:
     3.82:1 where the chevron version was 3.36:1. Holding the old width would
     have rendered it 5px shorter and quietly shrunk the header's optical
     weight. This width keeps the RENDERED HEIGHT at the 44px the header's
     vertical rhythm was tuned against. Re-solve it if the artwork changes
     shape again -- width alone is not the thing being held. */
  width: 168px;
  height: auto;
  /* 122/148 = 0.824. Scaling from the left keeps the lockup pinned to the
     gutter instead of drifting inward as it shrinks. */
  transform-origin: left center;
  transition: transform var(--l99-dur-transition) var(--l99-ease-standard);
}
.site-header[data-stuck] .site-header__logo img { transform: scale(0.824); }

/* ---- Primary nav -------------------------------------------------------- */
.site-nav__list {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  list-style: none;
  margin: 0;
  padding: 0;
}
.site-nav__list a:not(.btn) {
  display: block;
  padding-block: var(--space-2);
  font-family: var(--l99-font-signage);
  font-size: var(--text-md);
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--cream);
  text-decoration: none;
  /* Same thickness as the active state, so nothing shifts when it changes. */
  border-bottom: var(--rule-thick) solid transparent;
}
.site-nav__list a:not(.btn) {
  transition: color var(--l99-dur-micro) var(--l99-ease-standard),
              border-bottom-color var(--l99-dur-micro) var(--l99-ease-standard);
}
.site-nav__list a:not(.btn):hover { border-bottom-color: var(--mint); color: var(--mint); }
.site-nav__list a:not(.btn):active { color: var(--white); }
/* :not(.btn) matches the two rules above it. Without the guard this also hit the
   Contact CTA on contact-us.html, flipping its label from white to ink -- which
   the palette migration turned into a measurable 3.24:1 failure. */
.site-nav__list a[aria-current='page']:not(.btn) {
  border-bottom-color: var(--mint);
  color: var(--mint);
}
/* 44px, matching the minimum enforced on every other target on the site. */
.site-nav .btn { min-height: 44px; padding-inline: var(--space-6); }
/* The header is forest, so the CTA takes the dark-ground pairing: mint surface,
   forest label (7.34:1). Trail-on-forest would be 3.12 and green on green. */
.site-nav__cta .btn--primary { background: var(--mint); color: var(--forest); }
.site-nav__cta .btn--primary:hover { background: var(--cream); color: var(--forest); }

.site-nav__toggle { display: none; }

@media (max-width: 60em) {
  .site-nav__toggle {
    display: grid;
    place-items: center;
    width: 48px;
    height: 48px;
    margin-right: calc(var(--space-2) * -1);
    color: var(--ink);
  }
  /* The drawer is fixed at --z-drawer and spans the right 315px, which is
     exactly where this button sits. Without its own stacking position the
     drawer paints straight over the close X: the icon swap below still runs,
     but the control is invisible and elementFromPoint returns the drawer. On a
     phone that leaves the scrim as the only way out, because there is no Esc
     key. Must stay above --z-drawer. */
  .site-nav__toggle { position: relative; z-index: calc(var(--z-drawer) + 1); }
  .site-nav__toggle[aria-expanded='true'] { color: var(--paper); }
  .site-nav__toggle svg { width: 24px; height: 24px; stroke: currentColor; stroke-width: 1.75; fill: none; }
  .site-nav__toggle[aria-expanded='true'] .site-nav__bars { display: none; }
  .site-nav__toggle:not([aria-expanded='true']) .site-nav__close { display: none; }

  .site-nav__list {
    position: fixed;
    inset: 0 0 0 auto;
    width: min(21rem, 84vw);
    z-index: var(--z-drawer);
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: var(--space-6);
    padding: var(--space-16) var(--space-8);
    background: var(--green-deep);
    transform: translateX(100%);
    transition: transform var(--l99-dur-transition) var(--l99-ease-standard);
  }
  .site-nav__list[data-open] { transform: translateX(0); }

  /* The drawer paints the same deep green the focus ring is drawn in, so
     without this every link and the CTA focus invisibly. */
  .site-nav__list { --l99-ring: var(--paper); }

  .site-nav__list a:not(.btn) {
    font-size: var(--text-2xl);
    letter-spacing: 0.04em;
    color: var(--paper);
  }
  .site-nav__list a:not(.btn):hover { border-bottom-color: var(--green-bright); }
  .site-nav .btn--primary {
    background: var(--green-bright);
    color: var(--green-deep);
    margin-top: var(--space-2);
  }
  .site-nav .btn--primary:hover { background: var(--paper); color: var(--green-deep); }

  .nav-scrim {
    position: fixed;
    inset: 0;
    z-index: calc(var(--z-drawer) - 1);
    background: rgba(26, 26, 23, 0.5);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--l99-dur-transition) var(--l99-ease-standard);
  }
  .nav-scrim[data-open] { opacity: 1; pointer-events: auto; }
}

/* ---- Site footer -------------------------------------------------------- */
/* Every other seam on the page is one section's padding-bottom plus the next
   one's padding-top. The footer was getting a third helping from this margin,
   which measured 158px against 105px everywhere else. Zeroed below when a
   <main> precedes it — which is every page — and kept as a fallback otherwise.
   The selector has to key off main: the footer is main's sibling, not a
   section's. */
.site-footer {
  margin-top: var(--section);
  padding: var(--section) var(--gutter) var(--space-12);
  background: var(--forest);
  color: var(--cream);
  --l99-ring: var(--mint);
}
main + .site-footer { margin-top: 0; }
.site-footer a {
  color: var(--mint);
  transition: color var(--l99-dur-micro) var(--l99-ease-standard);
}
.site-footer a:hover  { color: var(--white); }
.site-footer a:active { color: var(--white); opacity: 0.7; }

.site-footer__inner {
  max-width: var(--container);
  margin-inline: auto;
  display: grid;
  gap: var(--space-16);
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
}
@media (max-width: 52em) {
  .site-footer__inner { grid-template-columns: 1fr; gap: var(--space-12); }
}

.site-footer h2 {
  font-size: var(--text-3xl);
  letter-spacing: 0.02em;
  color: var(--cream);
  margin-bottom: var(--space-4);
}
.site-footer__lede { color: var(--cream); }

.site-footer__cols {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-8);
}
.site-footer__cols h3 {
  font-size: var(--text-base);
  letter-spacing: 0.13em;
  color: var(--cream);
  margin-bottom: var(--space-4);
}
.site-footer__cols ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }
.site-footer__cols a { text-decoration: none; border-bottom: 1px solid transparent; }
.site-footer__cols a:hover { border-bottom-color: var(--mint); }

/* On touch these links measured 21px tall against a 44px minimum. Grow the hit
   area with padding and pull the list gap in, so the column ends up the same
   height it was rather than half a screen longer. */
@media (max-width: 52em) {
  .site-footer__cols ul { gap: 0; }
  .site-footer__cols a {
    display: inline-block;
    padding-block: 12px;
    border-bottom: 0;
  }
  .site-footer__cols a:hover { text-decoration: underline; }
}

.site-footer__legal {
  max-width: var(--container);
  margin: var(--space-16) auto 0;
  padding-top: var(--space-6);
  border-top: var(--rule-hair) solid color-mix(in srgb, var(--cream) 26%, var(--forest));
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-8);
  justify-content: space-between;
  font-size: var(--text-sm);
  color: color-mix(in srgb, var(--cream) 74%, var(--forest));
}

.site-footer__legal-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-6);
  margin-top: var(--space-3);
}

/* ---- Mailing list ------------------------------------------------------- */
.signup { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-6); }
.signup__field { flex: 1 1 15rem; }
.signup label {
  display: block;
  font-size: var(--text-sm);
  margin-bottom: var(--space-2);
  color: var(--cream);
}
.signup input {
  width: 100%;
  min-height: 48px;
  padding: var(--space-3) var(--space-4);
  background: var(--paper);
  /* green-deep on brand green is 5.70:1, well clear of the 3:1 UI minimum. */
  border: 1px solid var(--green-deep);
  border-radius: var(--radius-block);
  color: var(--ink);
}
/* --ink-soft, matching .field below. The two placeholder colours were
   --ink-soft here and --ink-faint there, for the same role in the same kind of
   control. Both clear 4.5:1 on paper, so this is drift rather than a defect --
   but it is the kind that makes a later "why is this one lighter?" expensive. */
.signup input::placeholder { color: var(--ink-soft); }
.signup input:focus-visible { outline-color: var(--green-deep); }
.signup .btn--primary { background: var(--green-deep); color: var(--paper); align-self: flex-end; }
.signup .btn--primary:hover { background: var(--ink); color: var(--paper); }

/* ---- Forms --------------------------------------------------------------- */
.form { display: grid; gap: var(--space-6); max-width: 34rem; }
.form--wide { max-width: none; }

.field { display: grid; gap: var(--space-2); }
.field > label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink-soft);
}
.field__req { color: var(--ochre); }

/* Labels are the only thing telling you what a field is, so on a phone they
   come up to body size rather than staying at the 14px desktop caption size. */
@media (max-width: 52em) {
  .field > label,
  .signup label { font-size: var(--text-base); }
}

.field input,
.field select,
.field textarea {
  width: 100%;
  min-height: 48px;
  padding: var(--space-3) var(--space-4);
  background: var(--paper);
  border: 1px solid var(--ink-faint);
  border-radius: var(--radius-block);
  color: var(--ink);
}
.field textarea { min-height: 8rem; resize: vertical; }
.field input::placeholder, .field textarea::placeholder { color: var(--ink-soft); }

/* Errors are announced and sit under the field they belong to, not in a
   summary at the top where you have to hunt for the cause. */
.field__error {
  font-size: var(--text-sm);
  color: var(--ochre);
  min-height: 1.2em;
}
.field[data-invalid] input,
.field[data-invalid] select,
.field[data-invalid] textarea { border-color: var(--ochre); border-width: 2px; }

.form__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); }

/* Not opacity. At 0.5 the "Sending..." label fell to roughly 2:1 on the brand
   block -- unreadable at the one moment the user needs to read it. Desaturating
   the surface keeps the text at full strength. */
.form button[disabled],
.form button[disabled]:hover {
  cursor: progress;
  background: var(--paper-edge);
  color: var(--ink-soft);
  transform: none;
}
.section--brand .form button[disabled],
.section--brand .form button[disabled]:hover {
  background: color-mix(in srgb, var(--cream) 34%, var(--forest));
  color: var(--cream);
}

.form__note { font-size: var(--text-sm); color: var(--ink-soft); }

/* Forms live on the brand block as well as on paper. That block used to be a
   light mint sheet; it is forest now, so every label, note and border here
   resolves LIGHT. The inputs keep a white field -- typing into a dark box is
   worse than the contrast saved. */
.section--brand .field > label,
.section--brand .choice legend,
.section--brand .form__note { color: var(--cream); }
.section--brand .field__req { color: var(--mint); }
.section--brand .form__note a { color: var(--mint); }
.section--brand .field__error { color: var(--mint); font-weight: 600; }
.section--brand .field input,
.section--brand .field select,
.section--brand .field textarea,
.section--brand .signup input { background: var(--white); border-color: var(--white); color: var(--ink); }
.section--brand .field[data-invalid] input,
.section--brand .field[data-invalid] select,
.section--brand .field[data-invalid] textarea { border-color: var(--ochre); }
.section--brand .field input::placeholder,
.section--brand .signup input::placeholder { color: var(--ink-soft); }
.section--brand .check span,
.section--brand .choice__option span { color: var(--cream); }
.section--brand .signup label { color: var(--cream); }

/* Radio and checkbox groups */
.choice { display: grid; gap: var(--space-3); }
.choice legend {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink-soft);
  margin-bottom: var(--space-2);
  padding: 0;
}
.choice__option {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  min-height: 44px;
  padding-block: var(--space-2);
}
/* Same size and same accent as .check below. These were 1.15rem with no
   accent-color, so the MailBag radios rendered in the operating system's blue
   on a brand block while the checkbox two components away was brand green. */
.choice__option input {
  width: 1.35rem;
  height: 1.35rem;
  min-height: 0;
  margin-top: 0.15em;
  flex: none;
  accent-color: var(--green);
}
.choice__option span { color: var(--ink); }
fieldset.choice { border: 0; margin: 0; padding: 0; }

/* ---- Checkbox in a form ------------------------------------------------
   Wrapping label, so the text is part of the target rather than a 16px box
   next to it. */
.field--check { margin-bottom: var(--space-6); }
.check {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  min-height: 44px;
  padding-block: var(--space-2);
  cursor: pointer;
  font-weight: 400;
}
.check input {
  flex: none;
  width: 1.35rem;
  height: 1.35rem;
  margin-top: 0.15em;
  accent-color: var(--green);
}
.check span { line-height: 1.45; }

/* ---- Phone in the header ------------------------------------------------
   Pushed hard against the nav rather than centred, so it reads as a utility
   rather than a third navigation item. Below 60em the label drops and the
   icon alone remains -- a 44px target that sits beside the menu button. */
.site-header__phone {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-left: auto;
  min-height: 44px;
  padding-inline: var(--space-2);
  font-family: var(--l99-font-signage);
  font-size: var(--text-sm);
  letter-spacing: 0.06em;
  color: var(--cream);
  text-decoration: none;
  white-space: nowrap;
}
.site-header__phone svg {
  width: 1.15rem;
  height: 1.15rem;
  flex: none;
  fill: var(--mint);
}
.site-header__phone:hover { color: var(--mint); }
.site-header__phone:hover svg { fill: var(--mint); }

@media (max-width: 60em) {
  .site-header__phone { margin-left: auto; margin-right: var(--space-1); }
  .site-header__phone span { display: none; }
  .site-header__phone svg { width: 1.4rem; height: 1.4rem; }
}


/* ---- The signup on forest ----------------------------------------------
   It sits in the footer, which is a dark ground now. The field stays white --
   typing into a dark box is worse than the contrast it saves. */
.site-footer .signup input {
  background: var(--white);
  border-color: var(--white);
  color: var(--ink);
}
.site-footer .signup input::placeholder { color: var(--ink-soft); }
.site-footer .signup input:focus-visible { outline-color: var(--mint); }

/* ==========================================================================
   Polaroid frame

   The hero deals photographs onto the page as prints. This is the same object
   at section scale, so a trip photograph reads as something laid on the page
   rather than a rectangle cut into it — which is the whole point of the
   expedition-poster direction.

   It also gives the caption and the credit somewhere to live. Percentage
   padding resolves against the frame's own width, so the proportions hold at
   any size, exactly as they do in the hero.
   ========================================================================== */
.polaroid {
  margin: 0;
  background: var(--polaroid);
  padding: 3.2% 3.2% 0;
  box-shadow: var(--shadow-print);
  border-radius: 2px;
  /* Barely off square. Enough to read as a physical object, small enough that
     a full-width photograph does not look like a mistake. */
  transform: rotate(-0.55deg);
}
.polaroid picture,
.polaroid img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 1px;
}

/* The deep foot margin, and what lives in it.

   Sized against the FRAME, not the viewport. Handwriting does not rescale
   itself per photograph, so the caption has to be a fixed fraction of the
   object it is written on -- otherwise the same hand reads at 5% of a big
   polaroid and 10.7% of a small one, which is what stops the whole thing
   looking like a polaroid. --text-hand was viewport-based and did exactly that.

   3.9cqw is solved from line fill: at --text-hand the longest caption on the
   site ran 80% of the frame width, edge to edge, which reads as a printed label
   rather than something jotted in the foot. This puts it near 60% and leaves
   the white margin either side that sells the object. Floor and ceiling keep a
   thumbnail legible and stop a full-bleed frame shouting. */
.polaroid {
  container-type: inline-size;
  /* Named once so the trips.css override can point at the same solve rather
     than restating the numbers and drifting from them. */
  --polaroid-cap: clamp(1.15rem, 3.9cqw, 1.6rem);
}
.polaroid figcaption {
  display: block;
  padding: 4.5% 1% 5.5%;
  font-family: var(--l99-font-hand);
  font-size: var(--polaroid-cap);
  line-height: 1.2;
  letter-spacing: 0.005em;
  /* Ink on white stock, never the surrounding ground — a polaroid on the
     forest block still has a white foot. */
  color: var(--ink-pen);
  transform: rotate(0.45deg);
}

/* KEPT DELIBERATELY (Anthony, 2026-08-24). A design review flagged this as a
   false affordance: these figures lift on hover and are not clickable. The
   decision is to keep it, and the reason it is defensible is that the site now
   has two distinct hover gestures, not one:

     decorative polaroid  ->  straightens and lifts
     interactive trip photo -> zooms inside a clipped frame  (css/trips.css)

   Neither sets cursor: pointer, so nothing here claims to be a control. Do not
   "fix" this again without asking -- it has been raised and settled once. */
@media (hover: hover) {
  .polaroid { transition: transform var(--l99-dur-transition) var(--l99-ease-standard); }
  .polaroid:hover { transform: rotate(0deg) translateY(-4px); }
}

/* Norwester measures ch on its own zero, which is narrow, so an inherited
   68ch is a much shorter physical line than the body face intends. base.css
   documents this trap and guards it twice; these are the ones it missed. */
.trip-card__dates,
.trip-status { max-width: none; }

/* ---- Button row ----------------------------------------------------------
   A row of buttons, twelve times over, was a <p> carrying the same three
   inline declarations. Two problems with that beyond the repetition. A <p> is
   a paragraph, and this is a group of controls. And `p { max-width: 68ch }` in
   base.css applied to it, silently capping the row -- the same 68ch trap that
   file already documents and fixes twice elsewhere.

   .masthead__actions and .form__actions were already this component under
   different names; they can fold in whenever either is next touched. */
.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  max-width: none;
  margin-top: var(--space-8);
}
.actions--wide   { margin-top: var(--space-12); }
.actions--center { justify-content: center; }
/* For a row whose spacing already comes from the element above it -- a
   countdown, or the top of its own grid cell. */
.actions--tight  { margin-top: 0; }
