

/* ==========================================================================
   Tokens
   ========================================================================== */
:root {
  --bg:            #ffffff;
  --text:          #000000;
  --line:          #dedede;
  --line-strong:   #c6c6c6;
  --placeholder:   #e4e4e4;
  --muted-text:    #555555;

  --font-body:     'Manrope', Helvetica, Arial, sans-serif;
  --font-display:  'Manrope', Helvetica, Arial, sans-serif;
  --font-hanken:   'Manrope', Helvetica, Arial, sans-serif;
  --font-serif:    'Libertinus Math', 'Times New Roman', Times, serif;

  --header-h:      44px;
  --gutter:        18px;

  --work-flex:     1;
  --info-flex:     1;

  --site-header-h: 88px;
  --lang-footer-h: 52px;
  --side-nav-h: 56px;
  --mobile-page-top: var(--gutter);

  --transition-theme: background-color .25s ease, color .25s ease, border-color .25s ease;

  --accent: #8a2e22;

  /* Right column (.info) colors on desktop — inverted relative to the
     main site theme so it reacts when the light/dark toggle is used. */
  --info-bg:          #000000;
  --info-text:        #ffffff;
  --info-line:        rgba(255, 255, 255, 0.25);

  --font-info: 'Manrope', Helvetica, Arial, sans-serif;

  /* Thinner black outline used on mobile buttons (nav tabs, language
     switch) — a lighter-weight version of the outline used elsewhere
     on the site's yellow buttons. Kept for reference; the outline
     itself is now drawn with -webkit-text-stroke (see .site-nav-btn
     and .lang-switch--header .lang-btn) instead of stacked
     text-shadows, since the shadow copies overlap and fill in solid
     glyphs like INFO/WORK/CONTACT/CV while staying thin and hollow
     on shorter ones like EN/FR/ES — a real outline stroke renders
     the same width on every letterform. */
  --btn-outline-thin: -0.5px -0.5px 0 #000, 0.5px -0.5px 0 #000, -0.5px 0.5px 0 #000, 0.5px 0.5px 0 #000;
  --btn-outline-stroke: 0.6px #000;
}

/* Dark mode — toggled by adding data-theme="dark" to <html> via script.js */
:root[data-theme="dark"] {
  --bg:            #121212;
  --text:          #ededed;
  --line:          #2e2e2e;
  --line-strong:   #444444;
  --placeholder:   #2a2a2a;
  --muted-text:    #a8a8a8;
  --accent:        #e08a72;

  --info-bg:       #ffffff;
  --info-text:     #000000;
  --info-line:     rgba(0, 0, 0, 0.2);
}

body,
.site-header,
.lang-footer,
.stream-image-frame,
.scroll-top-btn {
  transition: var(--transition-theme);
}

/* ==========================================================================
   Reset
   ========================================================================== */
* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  overflow-x: hidden;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.2;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p { margin: 0; }

p { font-family: var(--font-serif); }

a,
a:link,
a:visited,
a:hover,
a:active {
  color: var(--text);
  text-decoration: none;
}

button {
  font-family: inherit;
  font-size: inherit;
  color: var(--text);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

img { display: block; max-width: 100%; }

:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}

/* ==========================================================================
   Layout — desktop: two columns, 2:1 ratio, each scrolling independently
   ========================================================================== */
.layout {
  display: flex;
  align-items: stretch;
  height: 100vh;
}

.work {
  flex: var(--work-flex) 1 0;
  min-width: 0;
  height: 100vh;
  overflow-y: auto;
}

.info {
  flex: var(--info-flex) 1 0;
  min-width: 0;
  height: 100vh;
  overflow-y: auto;
  background-color: #f2f2f2;
}

/* ==========================================================================
   Desktop — no header menu at all; the stream starts right at the
   top of the work column.
   ========================================================================== */

/* ==========================================================================
   Language toggle
   ========================================================================== */
.lang-switch {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--line-strong);
}

.lang-btn { padding: 2px 0; }
.lang-btn:hover { color: #000000; }
.lang-btn[data-active="true"] {
  color: var(--text);
}

/* Variant used in the mobile site header — the buttons themselves
   now carry .site-nav-btn / .site-nav-btn--lang-style directly (see
   index.html), so they're styled identically to Info/Work/Contact/CV
   by sharing the same rules, not by duplicating matching values here. */
.lang-switch--header {
  margin: 0 20px;
  gap: 4px;
}

/* The "/" separators between EN/FR/ES aren't buttons, so they don't
   pick up .site-nav-btn's color on their own. */
.lang-switch--header span[aria-hidden="true"] {
  color: #000000;
}

/* Elements with a French/English/Spanish set — visibility toggled by
   script.js (setLanguage/applyLanguage), one shown at a time. Browsers
   restart a CSS animation whenever an element goes from display:none
   back to visible, so switching language re-triggers this fade on
   whichever set just became the active one — no JS timing needed. */
.lang-fr,
.lang-en,
.lang-es {
  animation: langFadeIn .35s ease;
}

.lang-fr,
.lang-en { color: var(--text); }

@keyframes langFadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Respect reduced-motion preferences — show the swapped language
   instantly instead of fading it in. */
@media (prefers-reduced-motion: reduce) {
  .lang-fr,
  .lang-en,
  .lang-es {
    animation: none;
  }
}

/* ==========================================================================
   Stream — all projects in the active category, stacked vertically.
   Each image sits inset inside a bordered frame for negative space.
   ========================================================================== */
.stream-item {
  padding: 24px 0 40px;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}

.stream-image-frame {
  position: relative;
  width: auto;
  max-width: 92%;
  height: 90vh;
  /* Fallback ratio (matches the 4:5 photos) used only until script.js
     measures the real media and sets --frame-ratio inline below —
     keeps the frame from flashing a wrong shape while it loads. */
  aspect-ratio: var(--frame-ratio, 4 / 5);
  margin: 0 auto;
  overflow: hidden;
  transition: var(--transition-theme), outline-color .15s ease;
  outline: 2px solid transparent;
  outline-offset: -2px;
  cursor: zoom-in;
}

/* Odd-numbered images (1st, 3rd, 5th...) in the homepage stream are
   shown larger than the even ones in between, for a bit of visual
   rhythm as you scroll. Higher specificity than the base rule above
   so it wins without needing !important. Width is capped, not fixed —
   the frame's real width comes from height (90vh) x aspect-ratio
   above, set per-project from the actual image/video dimensions. */
.stream-item:nth-child(odd) .stream-image-frame {
  max-width: 92%;
  height: 90vh;
}

.stream-item:nth-child(even) .stream-image-frame {
  max-width: 92%;
  height: 90vh;
}

.stream-image-frame img,
.stream-image-frame video {
  width: 100%;
  height: 100%;
  /* Frame's aspect-ratio now matches the media's real ratio (set in
     script.js), so cover and contain render identically in the
     normal case — no letterbox bars. cover (not contain) is kept as
     a safety net so a mismatched hover-swap image (e.g. a 16:9 video
     paired with a 4:5 photo) fills the frame rather than leaving
     bars, at the cost of a small crop only in that edge case. */
  object-fit: cover;
  display: block;
}

/* Blur-up while loading — script.js tags every stream image/video with
   .media-loading as soon as it's created, then removes the class the
   moment it has its first decodable frame (see onMediaReady in
   script.js). Scale is nudged up slightly while blurred so the blurred
   edges don't reveal the frame behind them. */
.stream-media {
  transition: filter 0.4s ease;
  filter: none;
}

.stream-media.media-loading {
  filter: blur(20px);
  transform: scale(1.03);
}

/* Casual image-copy deterrents (see the matching contextmenu/dragstart
   listeners in script.js) — blocks text/image selection, the native
   drag-out ghost, and iOS's long-press "Save Image" callout on project
   media. Doesn't touch anything else on the page (nav, buttons, etc.
   stay normally selectable). Doesn't stop screenshots or dev tools —
   nothing running in the page can. */
.stream-media,
.lightbox-media {
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
}

/* Auto-cycling gallery — every image/video for a project is stacked
   in the same frame and crossfaded automatically (see buildStreamCarousel
   in script.js). Only the active slide is visible; the rest wait
   underneath at opacity 0. Projects with just one image render a
   single slide that's simply always "active", with no runtime cost. */
.stream-carousel-slide {
  position: absolute;
  top: 0;
  left: 0;
  transition: transform 1.4s cubic-bezier(.65, 0, .35, 1);
  will-change: transform;
  pointer-events: none;
}

.stream-carousel-slide.active {
  z-index: 2;
}

/* Desktop hover swap — replaces the auto-cycling carousel for
   multi-image projects (see buildStreamCarousel in script.js). The
   first image renders normally in flow, the second sits absolutely
   positioned on top at opacity 0. On hover they cross-swap: the first
   fades out completely and the second fades in, so only one is ever
   visible at a time — never both. The frame's cursor switches to a
   plain pointer while this is active — no more zoom-in magnifier —
   but clicking still opens the lightbox with its usual zoom effect. */
.stream-hover-frame {
  cursor: pointer;
}

.stream-hover-base {
  transition: opacity .3s ease;
}

.stream-hover-frame:hover .stream-hover-base {
  opacity: 0;
}

.stream-hover-image {
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
  transition: opacity .3s ease;
  pointer-events: none;
}

.stream-hover-frame:hover .stream-hover-image {
  opacity: 1;
}

/* Lateral prev/next arrows — sit over the frame (absolutely
   positioned within .stream-image-frame, so they never take up
   margin/layout space), hidden until the visitor hovers or focuses
   it on desktop. Only present on projects with more than one image
   (see buildStreamCarousel in script.js). */
.stream-carousel-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: rgba(0, 0, 0, 0.45);
  color: #ffffff;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  z-index: 5;
  opacity: 0;
  pointer-events: none;
  transition: opacity .2s ease, background-color .2s ease;
}

.stream-image-frame:hover .stream-carousel-arrow,
.stream-carousel-arrow:focus-visible {
  opacity: 1;
  pointer-events: auto;
}

.stream-carousel-arrow:hover { background: rgba(0, 0, 0, 0.7); }

.stream-carousel-arrow--prev { left: 14px; }
.stream-carousel-arrow--next { right: 14px; }

/* Mobile: this carousel (and its arrows) only renders here in the
   first place — isDesktopGallery() in script.js swaps to a plain
   hover-swap with no arrows at all once the viewport crosses 801px.
   Touch devices have no reliable hover/focus state to trigger the
   reveal above, so on mobile the arrows stay visible all the time
   instead — still layered on top of the image via position: absolute
   above, not adding any margin or layout space. */
@media (max-width: 800px) {
  .stream-carousel-arrow {
    opacity: 1;
    pointer-events: auto;
  }
}

/* Title overlay removed — no longer shown on hover (or at all) over
   project thumbnails; .stream-meta below still shows each project's
   title beneath its image. */
.stream-image-title {
  display: none;
}

.stream-meta {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  margin-top: 24px;
  padding: 0 var(--gutter) 0 var(--gutter);
  gap: 10px;
}

.stream-info { max-width: 560px; width: 100%; margin: 0 auto; }

.stream-title {
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight:600;
}
.stream-collection {  font-family: var(--font-serif); font-size: 1.1rem; margin-top: 4px; color: #8e8e8e; }

.stream-desc-wrap {
  margin-top: 16px;
  max-width: 58ch;
  margin-left: auto;
  margin-right: auto;
}

.stream-desc-wrap .lang-fr,
.stream-desc-wrap .lang-en,
.stream-desc-wrap .lang-es {
  font-family: var(--font-serif);
  font-size: 1rem;
  line-height: 1.2;
  color: #000000;
}

/* ==========================================================================
   Desktop — single centered column of projects, same stacked list as
   mobile, just centered within the wider work column instead of
   running edge-to-edge. Active at desktop widths only.
   ========================================================================== */
@media (min-width: 801px) {
  .stream {
    max-width: 860px;
    margin: 0 auto;
  }

  /* Once the INFO/CV/CONTACT panel opens, the Work column narrows to
     roughly 55-60% of the screen (see --work-flex/--info-flex). On
     wide enough screens that column is still wider than 860px, so
     the cap above would leave the "full width" odd images capped at
     860px instead of truly reaching the column's edges. Drop the cap
     in that state so .stream fills the actual column width and the
     odd/even alternation below (nth-child rules) is edge-to-edge. */
  body.info-open .stream {
    max-width: none;
    margin: 0;
  }

  .stream-item {
    padding: 24px 0 60px;
  }

  /* First project only: a small gap below the fixed side-nav bar,
     matching the right column's margin, instead of the normal 24px
     top padding used for the rest of the stream. */
  #stream .stream-item:first-child {
    padding-top: 5px;
  }

  .stream-meta {
    display: flex;
    align-items: center;
    text-align: center;
    margin-top: 12px;
    padding: 0 14px;
  }

  .stream-info { max-width: none; margin: 0 auto; }

  /* Wider than the shared 58ch default (set above, still used on
     mobile) so each line holds roughly 10-11 words instead of 7-8.
     These descriptions run heavy on longer words (e.g. "collaborative",
     "eco-responsible", "biodegradable"), so 10-11 words measures out
     to ~90-97 characters here, not the ~65-70 a generic word-length
     estimate would suggest — sized against the actual copy instead. */
  .stream-desc-wrap {
    max-width: 90ch;
    text-align: justify;
    text-align-last: left;
    hyphens: auto;
    -webkit-hyphens: auto;
    -ms-hyphens: auto;
  }

  .stream-desc-wrap .lang-fr,
  .stream-desc-wrap .lang-en,
  .stream-desc-wrap .lang-es {
    font-size: 1.05rem;
  }
}


/* ==========================================================================
   Lightbox — full-screen zoom overlay for the homepage stream images
   (click any project image to open it here). Fixed over everything
   else on the page, with a fade + slight scale-in on open.
   ========================================================================== */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(85, 85, 85, 0.90);
  opacity: 0;
  transition: opacity .25s ease;
}

.lightbox[hidden] { display: none; }

.lightbox.visible { opacity: 1; }

.lightbox-stage {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 64px 88px;
  cursor: zoom-out;
}

.lightbox-media {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  cursor: default;
  transform: scale(.96);
  transition: transform .3s cubic-bezier(.25, .8, .25, 1);
}

.lightbox.visible .lightbox-media { transform: scale(1); }

.lightbox-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 52px;
  height: 52px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: rgba(255, 255, 255, 0.12);
  color: #ffffff;
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
  z-index: 2;
  transition: background-color .2s ease;
}
.lightbox-arrow:hover { background: rgba(255, 255, 255, 0.25); }
.lightbox-arrow[hidden] { display: none; }

.lightbox-arrow--prev { left: 20px; }
.lightbox-arrow--next { right: 20px; }

.lightbox-caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 22px;
  text-align: center;
  color: #ffffff;
  font-size: 13px;
  letter-spacing: .02em;
  opacity: .8;
  padding: 0 24px;
  pointer-events: none;
}

/* Underlying page shouldn't scroll while the lightbox is open. */
body.lightbox-open { overflow: hidden; }

@media (max-width: 800px) {
  .lightbox-stage { padding: 56px 20px; }
  .lightbox-arrow {
    width: 42px;
    height: 42px;
    font-size: 22px;
    background: #ffffff;
    color: #000000;
  }
  .lightbox-arrow:hover { background: #eaeaea; }
  /* Line up with the same 20px side padding used on .lightbox-stage
     above, so the arrows sit right at the image's margin instead of
     overlapping past it. */
  .lightbox-arrow--prev { left: 20px; }
  .lightbox-arrow--next { right: 20px; }
  .lightbox-caption { font-size: 12px; bottom: 14px; }
}


/* ==========================================================================
   Info column
   ========================================================================== */
.info-inner {
  padding: var(--gutter);
  max-width: 780px;
}

.info-bio {
  margin-top: 0;
  font-size: 19px;
  line-height: 1.2;
}

/* Centered section subtitles within the bio (e.g. "Philosophy",
   "Process" above their lorem ipsum paragraphs) — same font size and
   weight as the surrounding paragraph text (see the unified
   typography rule further down), just centered instead of left-aligned. */
.info-subtitle {
  text-align: left;
  font-size: 0.9rem;
  margin-top: 32px;
  margin-bottom: 10px;
}
.info-bio .info-subtitle-block .info-subtitle + p { margin-top: 0; }

.info-subtitle-block {
  margin-bottom: 40px;
}

@media (max-width: 800px) {
  .info-subtitle-block {
    margin-bottom: 28px;
  }
}

/* Services + Exploration Techniques sit side by side as two columns
   within the same wrapper, each keeping its own heading + list. The
   wrapper alone carries the top/bottom section spacing; the two
   .info-subtitle-block columns inside it have their own margin-bottom
   cancelled below so that spacing isn't doubled on top of it. */
.info-subtitle-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  margin-top: 32px;
  margin-bottom: 40px;
}
.info-subtitle-columns .info-subtitle-block {
  min-width: 0;
  margin-bottom: 0;
}
.info-subtitle-columns .info-subtitle {
  margin-top: 0;
}

@media (max-width: 800px) {
  .info-subtitle-columns {
    grid-template-columns: 1fr;
    gap: 0;
    margin-top: 24px;
    margin-bottom: 28px;
  }
}

/* Areas of expertise — 3x3 grid of items below its heading. Uses the
   same lang-fr/lang-en/lang-es toggle as everything else (script.js
   sets style.display on these directly, falling back to this grid
   display when shown). */
.areas-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  margin-top: 4px;
  text-align: left;
}
.areas-grid span {
  display: block;
  margin: 0;
}
@media (max-width: 800px) {
  .areas-grid {
    grid-template-columns: 1fr;
    gap: 0;
  }
}

/* Copyright line below the social links — sized to match the project
   titles shown in the left column's stream (see .stream-title).
   Selector needs the .info-bio parent + tag name (".info-bio p" from
   the unified typography rule further down otherwise outranks a
   plain .info-copyright class and silently wins). */
.info-bio p.info-copyright {
  font-size: 0.875rem;
  text-align: left;
  margin-top: 24px;
}

/* First paragraph of the bio — larger than the rest of the info copy. */
.info-bio p.bio-lead {
  font-size: 1.4rem;
}

/* Gap above the CV/contact rows is set later, in the "unified
   typography" section, alongside the row-title gap. */
.block-row {
  display: flex;
  gap: var(--gutter);
  padding: 24px 0;
  border-top: 1px solid var(--line);
}

/* The first row used to pick up extra space on top (info-block's old
   margin-top plus its own border/padding), making the gap above it
   noticeably bigger than the gap between the other rows. Removing the
   border here makes every gap in the column the same size. */
.block-row:first-child {
  border-top: none;
  padding-top: 0;
}

/* Desktop only: the last row's own bottom padding (24px, from the
   base .block-row rule) was stacking on top of .info-inner's bottom
   padding, making the CV page's bottom margin bigger than its top
   margin. Zeroing it here makes the two match, mirroring how
   :first-child already zeroes the top. */
@media (min-width: 801px) {
  .info-block .block-row:last-child {
    padding-bottom: 0;
  }
}

.block-row h2 {
  flex: 0 0 auto;
  width: 30%;
  font-size: 15px;
  font-weight: 500;
}

.block-content {
  flex: 1 1 auto;
  font-size: 15px;
}

.block-content p {
  margin: 0 0 16px;
}

.block-content p:last-child {
  margin-bottom: 0;
}


.contact-links { line-height: 1.9; }

.contact-link {
  text-decoration: underline;
  text-underline-offset: 2px;
  transition: color 0.2s ease;
}

.contact-link:hover {
  color: gray;
}

/* Two copies of the same links: --panel sits in the Contact block
   (desktop layout only), --bio sits at the end of the bio text
   (mobile layout only). */
.contact-links--bio { display: none; }

/* Desktop-only block sitting right under the bio's first paragraph:
   commissions note + E-mail/Instagram/LinkedIn links. There's no
   separate Contact page on desktop, so this is the only place those
   links show. Hidden by default (mobile keeps its own copy on the
   Contact page via .contact-commissions above); shown in the
   min-width: 801px rule further down. */
.info-commissions { display: none; }

@media (max-width: 800px) {
  .contact-links--panel { display: none; }
  .contact-links--bio {
    display: block;
    margin-top: 20px;
  }

  /* Higher specificity than the a/a:link/a:visited reset above, which
     would otherwise strip the underline back off. */
  .contact-links--bio .contact-link {
    text-decoration: underline;
    text-underline-offset: 2px;
  }

  /* Mobile only: drop the Areas of practice, Philosophy, and Process
     blurbs from the bio (each is wrapped in .info-subtitle-block —
     heading + all 3 language paragraphs). The copyright line further
     down stays visible. Desktop is unaffected. */
  .info-bio .info-subtitle-block {
    display: none;
  }

  /* Mobile only: left-align the copyright line and trim it down to
     just the "Coded on ..." date, dropping the "© Samantha Becerra."
     name portion (see the split spans in index.html). Desktop keeps
     the full centered line. */
  .info-bio p.info-copyright {
    text-align: left;
  }
  .info-copyright .copyright-name {
    display: none;
  }
}

/* Hidden by default (including mobile, where the Contact page already
   shows its own social links and .work itself is hidden entirely).
   Revealed on desktop unconditionally — see the always-on override
   below — so it's visible at the bottom of the work stream on every
   page, not just Contact. */
.work-social-links {
  display: none;
}

/* ==========================================================================
   Right column — desktop only: white background / black text,
   regardless of the site's light/dark mode. Sits beside the work
   stream as a normal two-column layout — the work stream stays
   visible on the left while this column is open on the right.
   ========================================================================== */
@media (min-width: 801px) {
  /* Right column is always open on desktop — script.js adds
     .info-open to <body> on load and never removes it, so the site
     is permanently two columns, defaulting to the INFO/bio view.
     INFO/CV/CONTACT switch which view shows inside this column
     rather than opening or closing it. */
  .info { display: none; }
  body.info-open .info { display: block; }

  /* CV page only: content is short, so the column was still being
     forced to the full 100vh height set on .info by default (with
     overflow-y: auto for scrolling), leaving a big empty gap below
     the last row — much bigger than the gutter above the first row.
     Sizing the column to its content instead (and swapping the
     leftover footer padding for the same gutter used at the top)
     makes the two margins match. Info and Contact keep the original
     full-height, independently-scrolling column.
     max-height keeps this column from ever growing past the
     viewport — without it, a CV long enough to exceed 100vh had no
     scroll boundary of its own (height: auto removes it), so the
     whole page scrolled instead and dragged the .work column along
     with it. Capping the height keeps the base overflow-y: auto in
     effect once content is actually taller than the viewport, so a
     long CV scrolls independently again, same as Info/Contact. */
  body.info-view-cv .info {
    height: auto;
    max-height: 100vh;
    align-self: flex-start;
    padding-bottom: var(--gutter);
  }

  /* The info panel shows the bio + social links (default, via INFO),
     the CV block (via the CV button), or the contact block (via the
     CONTACT button) — never more than one at once. */
  .info-block { display: none; }
  body.info-view-cv .info-block { display: block; }
  body.info-view-cv .intro-columns { display: none; }

  /* Slide-in transition between Info/CV/Contact — plays automatically
     any time one of these panels goes from display:none to
     display:block (switching to display:block always restarts a CSS
     animation), so no JS timing/orchestration is needed. Gated behind
     body.info-panel-anim (added in script.js just after the initial
     paint) so this doesn't also play once on page load, when
     .intro-columns is already visible by default. */
  @keyframes infoPanelSlideIn {
    from { opacity: 0; transform: translateX(28px); }
    to   { opacity: 1; transform: translateX(0); }
  }

  body.info-panel-anim .intro-columns,
  body.info-panel-anim .info-block {
    animation: infoPanelSlideIn 0.55s cubic-bezier(0.22, 1, 0.36, 1);
  }

  /* Commissions note + social links, shown right under the bio's first
     paragraph (see .info-commissions in index.html). There's no
     separate Contact page on desktop, so this is always visible
     whenever the bio itself is (i.e. whenever CV isn't open). */
  .info-commissions {
    display: block;
    margin-top: 20px;
  }

  /* Breathing room between the social links and whatever comes next
     (Services / Techniques d'exploration). */
  .info-commissions .contact-links--intro {
    margin-bottom: 40px;
  }

  /* Arrow before each social link's name — sits still by default,
     nudges right along with the link's own color hover animation. */
  .info-commissions .contact-link-arrow {
    display: inline-block;
    margin-right: 6px;
    transition: transform 0.2s ease;
  }

  .info-commissions .contact-link:hover .contact-link-arrow {
    transform: translateX(4px);
  }

  .info {
    background:#F8F8F8;
    color: #000000;
 
  }

  .info a,
  .info .lang-fr,
  .info .lang-en,
  .info .lang-es {
    color: #000000;
  }

  .info-inner {
    padding: 14px;
    line-height: normal;
  }

  .block-row {
    border-top-color: rgba(0, 0, 0, 0.2);
  }
  .contact-links { line-height: normal; }

  /* Higher specificity than the a/a:link/a:visited reset, which would
     otherwise strip the underline back off. */
  .info-commissions .contact-link,
  .contact-commissions .contact-link {
    text-decoration: underline;
    text-decoration-color: #000000;
    text-underline-offset: 2px;
  }

  /* The fillable contact form is normally mobile-only, styled against
     the page's light background — override its field/button colors
     here so it stays legible against the info column too. */
  .info .contact-field input,
  .info .contact-field textarea {
    color: #000000;
    border-bottom-color: rgba(0, 0, 0, 0.5);
  }

  .info .contact-submit {
    border-color: #000000;
    color: #000000;
    background: #ffffff;
  }

  .info .contact-submit:hover {
    background: transparent;
    color: #000000;
  }

  /* This copy of the links now shows on the Contact page on both
     mobile and desktop (see .contact-links--panel below, which is
     hidden on desktop now instead). */

  /* Sits at the bottom of the stream itself now (see index.html), so
     it scrolls into view naturally instead of floating as a fixed
     overlay — smaller text, no border/box around it. Shown on every
     page now (not just Contact) since the work stream itself never
     hides on desktop, regardless of which Info/CV/Contact view is
     open in the right column. */
  .work-social-links {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 24px;
    padding: 24px var(--gutter) var(--gutter);
  }

  .work-social-links .contact-link {
    display: inline-block;
    color: #999999;
    text-decoration: underline;
    text-underline-offset: 2px;
    font-size: 14px;
    transition: color 0.2s ease;
  }

  .work-social-links .contact-link:hover {
    color: #000000;
  }
}

/* ==========================================================================
   Mobile contact form — lives inside the sidebar contact block, but only
   ever shown on the mobile "Contact" page (see the max-width:800px rule
   further down).
   ========================================================================== */
.contact-form.mobile-contact-form { display: none; }

/* ==========================================================================
   Contact form — fillable fields, submits via a mailto: link built in
   script.js.
   ========================================================================== */
.contact-form {
  margin-top: 18px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 360px;
}

.contact-sent-message {
  margin-top: 18px;
  text-align: center;
}

.contact-sent-message p + p {
  margin-top: 14px;
}

.contact-sent-title {
  font-weight: 600;
}

.contact-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--line-strong);
}

.contact-field input,
.contact-field textarea {
  font-family: var(--font-body);
  font-size: 15px;
  letter-spacing: normal;
  text-transform: none;
  color: var(--text);
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--line-strong);
  padding: 8px 0;
  resize: vertical;
  transition: var(--transition-theme);
}

.contact-field input:focus,
.contact-field textarea:focus {
  outline: none;
  border-bottom-color: var(--text);
}

.contact-submit {
  align-self: flex-start;
  margin-top: 4px;
  padding: 8px 16px;
  border: 1px solid var(--text);
  font-size: 15px;
  letter-spacing: 0.03em;
  background: #ffffff;
  color: #000000;
  transition: var(--transition-theme);
}

.contact-submit:hover {
  background: transparent;
  color: var(--text);
}

/* ==========================================================================
   Mobile site header — Info/Work tabs, language switch, dark mode toggle.
   Hidden entirely on desktop; see the desktop overrides further down.
   Nav sits left; language + theme are grouped and centered as a unit.
   Fixed to the bottom of the screen rather than sticky at the top.
   ========================================================================== */
.site-header {
  display: none;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  height: var(--site-header-h);
  padding: 0 var(--gutter);
  border-top: 1px solid var(--line);
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  background: var(--bg);
  border-top: 1px solid var(--line);
  z-index: 30;
}

.site-nav {
  display: flex;
  gap: 32px;
  grid-column: 1;
  justify-self: start;
  margin-left: 20px;
}

.site-nav-btn {
  font-size: 0.8rem;
  font-weight: 500;
  padding: 4px 0;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #000000;
  border-bottom: 1px solid transparent;
}

.site-nav-btn[data-active="true"] {
  border-bottom-color: var(--text);
}

/* Info, Work, Contact, and CV take on the same plain-text look as
   the language switch — uppercase like the lang buttons, but no
   letter-spacing, no bold, and an underline (instead of a border)
   to mark the active page. */
.site-nav-btn--lang-style {
  padding: 2px 0;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: normal;
  border-bottom: none;
}

.site-nav-btn--lang-style:hover { text-decoration: underline; }

.site-nav-btn--lang-style[data-active="true"] {
  border-bottom-color: transparent;
  text-decoration: underline;
}

/* Language buttons specifically (EN/FR/ES in the mobile header) drop
   the shared underline treatment above — smaller, no underline, just
   a black hover/active state. Scoped to .lang-switch--header so the
   INFO/WORK/CV/CONTACT buttons sharing .site-nav-btn--lang-style keep
   their own underline-as-active-indicator look. */
.lang-switch--header .lang-btn {
  font-size: 0.9rem;
  text-decoration: none;
}
.lang-switch--header .lang-btn:hover {
  text-decoration: none;
  color: #000000;
}
.lang-switch--header .lang-btn[data-active="true"] {
  text-decoration: none;
}

.header-controls {
  grid-column: 3;
  justify-self: end;
  display: flex;
  align-items: center;
  gap: 16px;
}

/* ==========================================================================
   Desktop — fixed horizontal navigation bar, sized to exactly match
   the left (.work) column's width — 50%, same ratio as
   --work-flex/--info-flex below — so the white bar sits only over the
   left column and the right (.info) column has no bar over it at
   all. Languages sit at the left edge of the bar; INFO / CV sit at
   the right edge (space-between). INFO/CV still open the same right-hand .info
   column as before (see script.js) — only position and layout
   changed, not click behavior. Label swaps between the open word and
   (X)CLOSE based on aria-expanded, same as before. Hidden entirely on
   mobile (that breakpoint has its own Info/Work switch in the site
   header instead).
   ========================================================================== */
.side-nav {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 50%;
  z-index: 40;
  padding: 0 14px;
  height: var(--side-nav-h);
  background-color: white;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 40px;
}

@media (min-width: 801px) {
  .side-nav { display: flex; }

  /* Only the left (.work) column needs room reserved for the fixed
     bar above it — padding-top instead of shifting the whole .layout
     down, since box-sizing: border-box (see Reset, above) keeps its
     total height at 100vh either way. The right (.info) column is
     left alone and runs flush to the very top of the screen, so
     there's no separate white strip sitting above it where the bar
     doesn't reach (it only spans the left column's width). */
  .work { padding-top: var(--side-nav-h); }
}

.side-nav-langs {
  display: flex;
  align-items: center;
  gap: 6px;
}

/* Unselected language sits at a medium grey; the active one (set via
   data-active by applyLanguage() in script.js, same attribute the
   rest of the site already uses) stays black. */
.side-nav-langs .lang-btn,
.side-nav-langs span[aria-hidden="true"] {
  color: #888888;
  font-size: 1.1rem;
  font-weight: 500;
}

.side-nav-langs .lang-btn:hover {
  color: #000000;
}

.side-nav-langs .lang-btn[data-active="true"] {
  color: #000000;
}

.side-nav-links {
  display: flex;
  flex-direction: row;
  gap: 28px;
}

/* Unselected by default — medium grey. Whichever view is actually
   showing gets black text instead (see rules below); INFO counts as
   selected whenever CV's view isn't open, since it's the default/home
   view and never carries its own aria-expanded state. */
.side-nav-btn {
  font-size: 1.1rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: normal;
  text-align: left;
}

.side-nav-btn:hover {color:gray; }
.side-nav-btn[aria-expanded="true"] { text-decoration: underline;}

body:not(.info-view-cv) #infoToggleBtn,
body.info-view-cv #cvToggleBtn {
  color: #000000;
}

/* CV button: medium grey while not the active view, black on hover
   (overriding the general .side-nav-btn:hover grey above) and while
   active (see the shared rule just above, which wins on specificity). */
#cvToggleBtn {
  color: #888888;
}

#cvToggleBtn:hover {
  color: #000000;
}

/* Scroll-to-top button is unrelated to the nav bar; it's now enabled
   on desktop again (see .scroll-top-btn below for its left-side
   position). */

body.info-open .stream-item:nth-child(odd) .stream-image-frame,
body.info-open .stream-item:nth-child(even) .stream-image-frame {
  max-width: 100%;
  margin: 0;
}

/* ==========================================================================
   Desktop scroll-to-top button — fixed to the left side of the
   screen, hidden until scrolled down. Circular, dark grey background
   with a black arrow, same in both light and dark mode. Kept as a
   solid shape (no blend mode) since, unlike the text-only controls,
   it needs to stay legible and consistent over any image behind it.
   ========================================================================== */
.scroll-top-btn {
  position: fixed;
  left: 28px;
  bottom: 82px;
  width: 50px;
  height: 50px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: lightgrey;
  color: black;
  font-size: 21px;
  z-index: 40;
  opacity: 0;
  transform: translateY(8px);
  pointer-events: none;
  transition: opacity .2s ease, transform .2s ease, background-color .2s ease, color .2s ease;
}

.scroll-top-btn:hover { background: #3a3a3a; color: white; }

:root[data-theme="dark"] .scroll-top-btn {
  background: #4d4d4d;
  color: #000000;
}

:root[data-theme="dark"] .scroll-top-btn:hover { background: #3a3a3a; }

.scroll-top-btn.visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* ==========================================================================
   Mobile Info / Work layout
   The Work stream now lives on the same page as Info, positioned right
   after the bio + contact links (via .info { order: 1 } / .work {
   order: 2 } below) rather than behind a separate "Work" tab. "CV" and
   "Contact" still open their own dedicated pages, on which Work stays
   hidden. Only applies below 800px — desktop always shows both columns
   side by side.
   ========================================================================== */
@media (max-width: 800px) {
  body.mobile-view-cv .work {
    display: none;
  }

  /* Home screen shows the bio, its contact links, and the Work stream
     below. "CV" opens a page with just the education/employment/
     awards/press/referrals rows (.cv-row). There's no separate Contact
     page on mobile any more — the commissions note + social links show
     under the bio instead (see .info-commissions). */
  .info-block { display: none; }
  .block-row { display: none; }

  body.mobile-view-cv .info-bio { display: none; }
  body.mobile-view-cv .info-block { display: block; }
  body.mobile-view-cv .cv-row { display: flex; }
}

/* ==========================================================================
   Responsive — single column below 800px
   ========================================================================== */
@media (max-width: 800px) {

  .site-header {
    display: grid;
    /* Match the right (.info) column's background on desktop (#f2f2f2)
       regardless of light/dark mode — so the mobile nav bar reads as
       the same surface as the Info/CV/Contact panel. */
    background: #f2f2f2;
  }

  .site-nav-btn {
    font-size: 0.95rem;
    font-weight: 600;
  }

  .layout {
    flex-direction: column;
    height: auto;
    padding-bottom: var(--site-header-h);
  }

  .work,
  .info {
    height: auto;
    overflow-y: visible;
  }

  /* Bio (+ its contact links) appears first on screen; Work (the
     project gallery) follows directly below it — see the mobile
     Info / Work layout block above for the CV/Contact exception. */
  .info { order: 1; }
  .work { order: 2; }

  .info {
    border-left: none;
    border-top: none;
  }

  /* INFO section on mobile always reads as a white page with black
     text, regardless of light/dark mode (which otherwise flips body
     text to a light color via var(--text) and would make it
     unreadable against this section's light background). */
  .info {
    background: #ffffff;
    color: #000000;
  }

  .info a,
  .info .lang-fr,
  .info .lang-en,
  .info .lang-es {
    color: #000000;
  }

  /* Commissions note + social links (normally desktop-only) also show
     on mobile now, right after the bio paragraphs and directly above
     the Services list — all stacked in the same single column. */
  .info-commissions {
    display: block;
    margin-top: 32px;
    margin-bottom: 8px;
  }

  .info-commissions .contact-links--intro {
    margin-bottom: 0;
    line-height: 2.2;
  }

  .info-commissions .contact-link-arrow {
    display: inline-block;
    margin-right: 6px;
  }

  /* The two rules above (further up this same media query) hide the
     commissions note text and every .info-subtitle-block by default —
     both written back when this content lived only on the desktop
     bio / mobile Contact page. Now that .info-commissions shows in
     the mobile bio too, bring its note text back, and single out the
     Services block (only) to show alongside it — Techniques
     d'exploration/Philosophy/Process stay hidden on mobile. */
  .info-bio .info-commissions .commissions-note {
    display: block;
    font-size: 1.25rem;
    line-height: 1.4;
  }

  .info-bio .info-subtitle-block--services {
    display: block;
  }

  /* Social links (E-mail/Instagram/LinkedIn) read as grey text with
     a black arrow in front of each — overrides the black-text rule
     set on all .info links further up this same media query. */
  .info-commissions .contact-link {
    color: #5c5c5c;
  }

  .info-commissions .contact-link-arrow {
    color: #000000;
  }

  /* Desktop-only chrome stays out of the way on mobile */
  .scroll-top-btn,
  .lang-footer { display: none !important; }

  .info-inner { max-width: none; }

  .stream-item { padding: var(--gutter) 0 32px; }
  .stream-image-frame { height: 68vh; margin: 0 auto 18px; padding: 12px; }

  /* Even images now match odd — same size, no alternating rhythm. */
  .stream-item:nth-child(odd) .stream-image-frame,
  .stream-item:nth-child(even) .stream-image-frame { width: 94%; height: 72vh; }

  .stream-meta {
    padding: 0 var(--gutter);
  }

  .stream-info { max-width: none; }

  /* Bio sizes to its content on mobile so the Work stream follows
     shortly below the text, with a small gap rather than none at all
     or being pushed down to fill the whole first screen. */
  .info-bio {
    display: flex;
    flex-direction: column;
    padding-top: var(--mobile-page-top);
    padding-bottom: var(--gutter);
    font-size: 21px;
    line-height: 1.5;
  }
  .info-bio p + p { margin-top: 16px; }

  /* Coded-by credit line moves to the very top of the mobile page,
     shown smaller and grey — a quiet footnote rather than a paragraph
     on par with the bio. order: -1 moves it above every other child
     of .info-bio without touching its place in the HTML (it stays
     last in the markup, still pinned to the bottom on desktop via
     margin-top: auto). */
  .info-bio p.info-copyright {
    order: -1;
    font-size: 0.85rem;
    color: #8e8e8e;
    margin-top: 0;
    margin-bottom: 24px;
  }

  /* Social links at the very bottom of the mobile page too — same
     light grey styling as the desktop work-stream footer. .work is
     already hidden entirely on the CV/Contact mobile pages (see the
     Mobile Info / Work layout block above), so this only ever shows
     on the default Work/About page. */
  .work-social-links {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 24px;
    padding: 24px var(--gutter) var(--gutter);
    font-size: 13px;
  }

  .work-social-links .contact-link {
    color: #999999;
    text-decoration: underline;
    text-underline-offset: 2px;
  }

}

@media (max-width: 800px) {
  .info-bio,
  .info-block {
    padding-left: var(--gutter);
    padding-right: var(--gutter);
  }
  /* info-inner's own base padding (var(--gutter) on all sides) was
     still contributing its top value here even though each page below
     already adds its own top padding — stacking the two doubled the
     top gap to 2x the gutter while the sides stayed at 1x. Zeroing
     info-inner's top (as already done for its left/right) leaves each
     page's own top padding as the only source, matching the sides. */
  .info-inner { padding-left: 0; padding-right: 0; padding-top: 0; }
}

/* ==========================================================================
   Right column — unified typography
   Everything in the info column (bio, CV/contact rows, contact form)
   now shares one font (Manrope) and one size (1.9rem), on both mobile
   and desktop, instead of the mix of serif/Hanken sizes used before.
   Placed at the end of the sheet so it wins over the earlier per-element
   sizes regardless of breakpoint.
   ========================================================================== */
.info-bio,
.info-subtitle,
.block-row h2,
.block-content,
.contact-links,
.contact-link,
.contact-field,
.contact-field input,
.contact-field textarea {
  font-family: var(--font-info);
  font-size: 1rem;
  font-weight: 600;
  line-height: 0;
}

/* Section heading (Formation/Education, Compétences/Skills, etc.) —
   line-height: 0 above pulls its text out of normal flow, so the
   first word no longer lines up with the first line of the content
   beside it. Give it the same line-height as that content
   (.block-content p, set further below) so both sit flush at the top. */
.block-row h2 {
  line-height: 1.2;
}

/* Paragraph/body copy (bio, philosophy/process, skills grid) — serif,
   everything else above (headings, nav, contact links/fields) stays
   sans-serif. Smaller than the intro paragraph below by default; the
   intro (first) paragraph of the bio is bumped back up right after,
   since it's the reference size the menu buttons now match. */
.info-bio p,
.areas-grid span {
  font-family: var(--font-serif);
  color: #5c5c5c;
  font-size:1.25rem;
  font-weight: 400;
  line-height: 1.2;
}

.info-bio > p.lang-fr,
.info-bio > p.lang-en,
.info-bio > p.lang-es {
  font-size: 1.45rem;
  margin-bottom: 40px;
}

/* First paragraph of the bio — tighter bottom margin than the rest. */
.info-bio > p.bio-lead-first.lang-fr,
.info-bio > p.bio-lead-first.lang-en,
.info-bio > p.bio-lead-first.lang-es {
  margin-bottom: 10px;
}

/* CV page + Contact page body copy — same serif treatment, but held
   to a fixed size regardless of viewport (see the mobile override
   below, which no longer resets this back up to 1rem). The Info bio's
   own paragraphs (including its first, introductory one) are excluded
   since they use .info-bio p above, not .block-content p. */
.block-content p {
  font-family: var(--font-serif);
  color: #8e8e8e;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.2;
}

/* CV page only: body text is black by default — grey is reserved for
   the year/date line at the end of each entry (see .cv-year below).
   Scoped to .cv-row so the Contact page keeps its own grey
   .block-content p treatment above untouched. */
.cv-row .block-content p {
  color: #000000;
}

.cv-year {
  color: #8e8e8e;
}

/* Skills section category labels (Software/Design/Web/Soft skills)
   switch to the same sans-serif treatment as the section subtitles
   (Education, Certifications, etc. — see .block-row h2), setting
   them apart from the serif list of items that follows. */
.cv-skill-label {
  font-family: var(--font-info);
  font-size: 0.9rem;
  font-weight: 600;
}

/* Languages list: no gap between each language line. */
.cv-languages .block-content p {
  margin-bottom: 0;
}

/* Mobile text size — 1rem across the app's body copy (bio, CV/contact
   rows, contact form, and project descriptions), overriding the
   desktop 1.2rem set above and the 15.5px project-text size below.
   Placed after both so it wins regardless of source order. */
@media (max-width: 800px) {
  .info-bio,
  .block-row h2,
  .block-content,
  .contact-links,
  .contact-link,
  .contact-field,
  .contact-field input,
  .contact-field textarea {
    font-size: 1rem;
  }

  .info-bio p {
    font-size: 0.75rem;
  }

  .areas-grid span {
    font-size: 1.25rem;
    line-height: 1.4;
  }

  .info-bio > p.lang-fr,
  .info-bio > p.lang-en,
  .info-bio > p.lang-es {
    font-size: 1.25rem;
    line-height: 1.4;
    margin-bottom: 0;
  }

  .block-content p {
    font-size: 1rem;
  }

  .info-subtitle {
    font-size: 1rem;
  }
}

.contact-field {
  text-transform: none;
  letter-spacing: normal;
}

/* Deliberate gap between the intro (contact + bio) and the CV rows
   below it, now that the first row itself carries no top border/padding.
   On desktop this comes from .intro-columns' own margin-bottom instead,
   since Contact and Bio are side by side there rather than stacked. */
.info-block {
  margin-top: 40px;
}

@media (min-width: 801px) {
  .info-block {
    margin-top: 0;
  }
}

/* Mobile: every page's top spacing matches var(--gutter), the same
   margin used on the images' left/right edges — regardless of how much
   left padding that particular page has. Contact has extra left padding
   (see "Mobile Contact page" rule above), but its top padding still
   uses the plain gutter so it lines up with the other pages. */
@media (max-width: 800px) {
  .info-block {
    margin-top: var(--mobile-page-top);
  }
}

/* More breathing room between each row's title and its content */
.block-row {
  gap: 40px;
}

@media (min-width: 801px) {
  .block-row {
    gap: 64px;
  }
}

/* ==========================================================================
   Desktop intro — Bio runs the full width of the column, starting at
   the same left margin the Contact column used to. The commissions
   note + social links now live right under the bio's first paragraph
   (see .info-commissions). There's no Contact page at all any more
   (desktop or mobile) — those links show under the bio everywhere.
   ========================================================================== */
@media (min-width: 801px) {
  /* Fill the full-height info column (see .info's 100vh + overflow-y:
     auto above) so the copyright line can be pinned to the very
     bottom of it via margin-top: auto below, instead of sitting
     wherever the bio text happens to end and leaving a gap beneath
     it. CV keeps its own shrink-to-content sizing (.intro-columns is
     display: none there), so this only affects the Info view. */
  .info-inner {
    min-height: 100%;
    display: flex;
    flex-direction: column;
  }

  .intro-columns {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    margin-bottom: 0;
  }

  .info-bio {
    flex: 1;
    display: flex;
    flex-direction: column;
  }

  /* Pushed to the bottom of the column, flush with .info-inner's own
     padding — same as the gutter on the left/right. padding-top
     guarantees a minimum gap above it even when the bio is long
     enough to leave little or no free space. */
  .info-bio p.info-copyright {
    margin-top: auto;
    padding-top: calc(var(--gutter) * 2);
  }
}

/* ==========================================================================
   Reduced motion
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}
