@charset "UTF-8";
/* ------------------------- DOCUMENT SETUP ------------------------- */
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
}

body {
  min-height: 100%;
  background-color: #ffffff;
  color: #444444;
  font-family: "Avenir Next", "Avenir", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  -webkit-text-stroke: 0.3px currentColor;
  text-align: left;
  text-rendering: optimizeLegibility;
  -webkit-text-size-adjust: 100%;
  -ms-text-size-adjust: 100%;
}

/* ------------------------- ARTICLE PAGES ------------------------- */
.document-wrapper {
  max-width: 48em;
  width: 100%;
  min-height: 100%;
  margin: 40px auto;
  padding: 0 2em 2em;
  color: #444444;
  font-family: "Avenir Next", "Avenir", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 15px;
  line-height: 1.755;
  tab-size: 4;
  /* Headings */
}
.document-wrapper h1, .document-wrapper h2, .document-wrapper h3, .document-wrapper h4, .document-wrapper h5, .document-wrapper h6 {
  display: block;
  font-weight: 600;
  line-height: 1.521;
}
.document-wrapper h1 {
  font-size: 2em;
  padding-top: 0.8em;
  margin-bottom: 1em;
}
.document-wrapper h2 {
  font-size: 1.6em;
  padding-top: 0.66em;
  margin-bottom: 0.7em;
}
.document-wrapper h3 {
  font-size: 1.27em;
  padding-top: 0.53em;
  margin-bottom: 0.25em;
}
.document-wrapper h4, .document-wrapper h5, .document-wrapper h6 {
  padding-top: 0.4em;
  padding-bottom: 0.27em;
}
.document-wrapper {
  /* Body text */
}
.document-wrapper p {
  display: block;
  margin-bottom: 1.8em;
}
.document-wrapper a {
  color: #0062FE;
  cursor: pointer;
  text-decoration: none;
}
.document-wrapper strong, .document-wrapper b, .document-wrapper th {
  font-weight: bold;
}
.document-wrapper em, .document-wrapper i {
  font-style: italic;
}
.document-wrapper {
  /* Lists */
}
.document-wrapper ul, .document-wrapper ol {
  display: block;
  padding-inline-start: 2.13em;
  margin-bottom: 1em;
}
.document-wrapper ul {
  list-style-type: disc;
}
.document-wrapper ul ul {
  list-style-type: circle;
}
.document-wrapper ul ul ul {
  list-style-type: disc;
}
.document-wrapper ol {
  list-style-type: decimal;
}
.document-wrapper li {
  display: list-item;
}
.document-wrapper li::marker {
  color: #DD4C4F;
}
.document-wrapper li > p {
  display: inline;
}
.document-wrapper {
  /* Quotes: an accent bar in the left gutter */
}
.document-wrapper blockquote {
  display: block;
  position: relative;
  padding-inline-start: 2.13em;
  margin-bottom: 1.8em;
}
.document-wrapper blockquote::before {
  content: "";
  position: absolute;
  top: -0.4em;
  left: 1em;
  width: 0.13em;
  height: calc(100% + 0.8em);
  background-color: #DD4C4F;
  border: 1px solid #DD4C4F;
  border-radius: 0.33em;
}
.document-wrapper hr {
  display: block;
  margin: 2em 0 0.5em;
  border: none;
  border-top: 1px solid #d9d9d9;
}
.document-wrapper {
  /* Code */
}
.document-wrapper code, .document-wrapper pre {
  font-family: "Menlo", ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.91em;
}
.document-wrapper code {
  padding: 0.25em;
  color: #444444;
  background-color: #F3F5F7;
  border-radius: 0.25em;
}
.document-wrapper pre {
  display: block;
  white-space: pre-wrap;
}
.document-wrapper pre code {
  display: block;
}
.document-wrapper {
  /* Tables */
}
.document-wrapper table {
  width: 100%;
  max-width: 100%;
  margin-bottom: 1.755em;
  border: 1px solid #d9d9d9;
  border-collapse: collapse;
}
.document-wrapper tr:nth-child(odd) {
  background-color: #F3F5F7;
}
.document-wrapper th, .document-wrapper td {
  min-width: 5em;
  padding: 0.37em 0.75em;
  text-align: left;
  border-right: 1px solid #d9d9d9;
}
.document-wrapper th:last-child, .document-wrapper td:last-child {
  border-right: none;
}
.document-wrapper {
  /* Media */
}
.document-wrapper img {
  display: inline;
  max-width: 100%;
}
.document-wrapper iframe {
  display: block;
  width: 100%;
  height: auto;
  margin-bottom: 20px;
  border: none;
  aspect-ratio: 660/440;
}
.document-wrapper iframe.portrait {
  aspect-ratio: 440/782;
  margin: 0 auto 20px;
}
.document-wrapper {
  /* Image size hints from the markdown: ![alt](photo.jpg#half) */
}
.document-wrapper img {
  width: 100%;
}
.document-wrapper img[src*="#half"] {
  width: 49%;
}
.document-wrapper img[src*="#twothirds"] {
  width: 66%;
}
.document-wrapper img[src*="#third"] {
  width: 32%;
}
.document-wrapper {
  /* Date pill at the end of a bearnote */
}
.document-wrapper .date {
  display: inline-block;
  margin-bottom: 38px;
  padding: 6px 10px;
  font-size: 14px;
  background-color: lightgray;
  border-radius: 7px;
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
}
.document-wrapper {
  /* Date under a post's title */
}
.document-wrapper .dateline {
  display: block;
  margin: -0.9em 0 1.8em;
  color: #888888;
}
.document-wrapper {
  /* Home link at the top and bottom of a post */
}
.document-wrapper #header {
  margin: 20px auto;
  text-align: center;
}
.document-wrapper #footer {
  margin-top: 30px;
  padding-top: 5px;
  font-size: 14.5px;
  color: #888888;
  text-align: center;
  border-top: 1px dotted #444444;
}
.document-wrapper .home-icon {
  display: inline-block;
  height: 38px;
  padding: 5px;
}
.document-wrapper .home-icon a {
  font-size: 30px;
  line-height: 1;
}
.document-wrapper .home-icon a:hover {
  opacity: 0.6;
}

/* ------------------------- COVER PAGE ------------------------- */
.hero {
  position: relative;
  display: flex;
  /* Center the photo+text group as a whole. Each side is sized to its content
     (not a fixed half), so the divider lands right of page-center rather than
     at a true centerline. */
  justify-content: center;
  align-items: flex-start; /* top-align photo and text */
  width: 100%;
  min-height: 100vh; /* fallback for older browsers */
  min-height: 100lvh; /* at least one screen */
  padding-bottom: 45px; /* a little air below the cover photo */
  background: #ffffff;
  color: #282828;
  font-size: 15px;
  line-height: 1.755;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Left side: a single cover photo. Sized to itself (not half the page) so the
   group centers as a whole; top-aligned to match the identity column. */
.hero-left {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  padding-right: 21px; /* half of the 42px gap to the identity column */
}

/* Tablet and desktop: start the photo level with the identity column's own
   initial offset (see the sticky `top` below), instead of flush against the
   top of the page. */
@media only screen and (min-width: 545px) {
  .hero-left {
    padding-top: calc(8vh + 35px);
  }
}
.cover-photo {
  display: block;
  width: 270px;
  height: auto;
  border-radius: 4px;
}

/* Mobile-only floated copy of the cover photo (see rides.html); hidden here,
   shown and positioned in the phone media query below. */
.cover-photo-mobile {
  display: none;
}

/* Right side: the identity column, sized to its content. */
.hero-right {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  padding-left: 21px; /* half of the 42px gap from the cover photo */
}

/* Tablet and desktop layouts: lock the identity column in place while the
   page scrolls, releasing at the bottom of the hero. The `top` also supplies
   the initial downward offset — 45px below the cover photo's own top (see
   .hero-left above), so the text reads as offset from the photo rather than
   flush with it. Scoped to >=545px so the sticky/top never leak onto the
   mobile stacked layout. */
@media only screen and (min-width: 545px) {
  .hero-right {
    position: sticky;
    top: calc(8vh + 35px + 45px);
    align-self: flex-start; /* content height, so it can stick (not stretched) */
  }
}
/* Identity block: a single left-aligned column on a white page, nudged
   slightly left of center and up. Shared by desktop and mobile; mobile
   only bumps the font sizes (see the phone media query). */
.hero-content {
  display: flex;
  flex-direction: column;
  z-index: 2;
  text-align: left;
  color: #282828;
}

/* Links match the rest of the site: link color, no underline. */
.entry a {
  color: #0062FE;
  text-decoration: none;
}

.nameField {
  font-size: 2em; /* same as a post's h1 */
  font-weight: 600;
  line-height: 1.521;
  margin-bottom: 22px;
  color: #282828;
}

.entry {
  margin-bottom: 15px;
  line-height: 1.5;
}

.titleline {
  opacity: 0.6;
}

.menu {
  margin-top: 23px;
  margin-left: -8px;
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 3px; /* the icons' own 5px side padding gives 10px between; +3px makes it 13px (1.3x) */
}

.menu-icon {
  display: inline-block;
  height: 38px;
  padding: 5px;
  color: #282828;
}

.menu-icon a {
  position: relative;
  display: inline-flex;
  align-items: center;
  text-decoration: none !important;
  /* Safari promotes this stacking context to its own compositing layer the
     first time a descendant's transform/opacity transition fires, and
     rounds the whole subtree — icon included — by a device pixel when it
     does. Forcing the layer to exist up front (instead of on first hover)
     sidesteps that snap; Chrome already layers eagerly so this is a no-op
     there. */
  transform: translateZ(0);
  backface-visibility: hidden;
}

.menu-icon svg {
  width: 26px !important;
  height: 26px !important;
  min-width: 26px; /* Prevent shrinking */
  min-height: 26px;
  color: #282828;
  transition: color 0.2s ease;
}

.menu-icon a:hover svg {
  color: #0062FE;
}

/* Wing-flap hover flutter for the Bluesky contact-card icon — adapted from
   Phil Hawksworth's "Flutterby" (MIT license):
   github.com/philhawksworth/bluesky-flutterby
   The icon is two copies of the same wing path; the right one is mirrored
   at rest, and each flaps independently (slightly different durations) on
   hover. */
/* transform-origin: center on an SVG <g> resolves against the viewBox in
   Chromium/Firefox but against the <g>'s own (half-width) bounding box in
   WebKit — there, the mirrored wing flips back on top of the original
   instead of across to the other side, rendering as half a butterfly.
   Pin both explicitly to the viewBox's actual center (566x500 → 283,250)
   so the mirror axis is unambiguous everywhere. */
/* --flip lives on the base (non-hover) rule since the one-time auto-flutter
   below animates .left/.right without hover ever being involved. */
.bluesky-flutter-icon .left {
  transform-box: view-box;
  transform-origin: 283px 250px;
  --flip: 1;
}

.bluesky-flutter-icon .right {
  transform-box: view-box;
  transform-origin: 283px 250px;
  transform: scale(-1, 1);
  --flip: -1;
}

.menu-icon:hover .bluesky-flutter-icon {
  transform: rotate(-5deg);
  transition: 500ms;
}

.menu-icon:hover .bluesky-flutter-icon .left {
  animation: bsky-flutter 430ms ease-in-out;
}

.menu-icon:hover .bluesky-flutter-icon .right {
  animation: bsky-flutter 500ms ease-in-out;
}

@keyframes bsky-flutter {
  10% {
    transform: scale(calc(var(--flip) * 1), 0.9);
  }
  20% {
    transform: scale(calc(var(--flip) * 0.5), 1);
  }
  40% {
    transform: scale(calc(var(--flip) * 0.9), 0.95);
  }
  60% {
    transform: scale(calc(var(--flip) * 0.3), 1);
  }
  80% {
    transform: scale(calc(var(--flip) * 0.9), 0.95);
  }
  100% {
    transform: scale(calc(var(--flip) * 1), 1);
  }
}
/* One unprompted flutter on load, 1s after the last entrance animation below
   finishes (LinkedIn icon: 0.86s delay + 0.6s duration = 1.46s), so the
   Bluesky icon draws a little attention once — tilt, wing-flap, and the hover
   blue, all together, then it settles back to plain :hover-only behavior
   (no fill-mode is set, so nothing lingers once the single run ends). */
@keyframes bsky-auto-flutter {
  0% {
    transform: rotate(0deg);
    color: #282828;
  }
  40% {
    transform: rotate(-5deg);
    color: #0062FE;
  }
  100% {
    transform: rotate(0deg);
    color: #282828;
  }
}
.bluesky-flutter-icon {
  animation: bsky-auto-flutter 500ms ease-in-out 2.46s 1;
}

/* Separate, identical-in-substance keyframes from .left/.right's hover
   animation (bsky-flutter) — reusing the same name here breaks the hover
   flutter afterwards: once this auto run finishes, hovering just changes
   the delay/iteration-count on that *same-named* animation instance rather
   than starting a new one, so its clock (already running since page load)
   is instantly "past due" and it snaps to the end pose with no visible
   flap. A distinct name keeps the two fully independent. */
@keyframes bsky-flutter-once {
  10% {
    transform: scale(calc(var(--flip) * 1), 0.9);
  }
  20% {
    transform: scale(calc(var(--flip) * 0.5), 1);
  }
  40% {
    transform: scale(calc(var(--flip) * 0.9), 0.95);
  }
  60% {
    transform: scale(calc(var(--flip) * 0.3), 1);
  }
  80% {
    transform: scale(calc(var(--flip) * 0.9), 0.95);
  }
  100% {
    transform: scale(calc(var(--flip) * 1), 1);
  }
}
.bluesky-flutter-icon .left {
  animation: bsky-flutter-once 430ms ease-in-out 2.46s 1;
}

.bluesky-flutter-icon .right {
  animation: bsky-flutter-once 500ms ease-in-out 2.46s 1;
}

/* Intro reveal: each line fades in while sliding left from the right; the
   contact icons follow one at a time. */
@keyframes heroSlideLeft {
  from {
    opacity: 0;
    transform: translateX(30px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}
.hero-content .nameField,
.hero-content .entry,
.hero-content .menu-icon {
  opacity: 0;
  animation: heroSlideLeft 0.6s ease-out forwards;
}

/* Stagger: name first, then each title line, then the icons individually. */
.hero-content .nameField {
  animation-delay: 0s;
}

.hero-content .entry:nth-of-type(2) {
  animation-delay: 0.15s;
}

.hero-content .entry:nth-of-type(3) {
  animation-delay: 0.3s;
}

.hero-content .entry:nth-of-type(4) {
  animation-delay: 0.45s;
}

.hero-content .menu .menu-icon:nth-of-type(1) {
  animation-delay: 0.62s;
}

.hero-content .menu .menu-icon:nth-of-type(2) {
  animation-delay: 0.74s;
}

.hero-content .menu .menu-icon:nth-of-type(3) {
  animation-delay: 0.86s;
}

.hero-content .menu .menu-icon:nth-of-type(4) {
  animation-delay: 0.98s;
}

/* Respect reduced-motion: show everything immediately, no slide. */
@media (prefers-reduced-motion: reduce) {
  .hero-content .nameField,
  .hero-content .entry,
  .hero-content .menu-icon {
    opacity: 1;
    animation: none;
  }
  .menu-icon:hover .bluesky-flutter-icon .left,
  .menu-icon:hover .bluesky-flutter-icon .right,
  .bluesky-flutter-icon,
  .bluesky-flutter-icon .left,
  .bluesky-flutter-icon .right {
    animation: none;
  }
}
/*--------------------------------- PHONE -------------------------------------------*/
@media only screen and (max-width: 544px) {
  /* Articles: a full-width column, and photos stack instead of sitting side by side.
     Bumped font-size and heading weight vs. desktop — the desktop 15px/600 values
     read noticeably smaller and lighter than the Bear app at phone width. */
  .document-wrapper {
    width: 95%;
    margin: 40px auto;
    padding: 0;
    font-size: 17px;
  }
  .document-wrapper h1, .document-wrapper h2, .document-wrapper h3, .document-wrapper h4, .document-wrapper h5, .document-wrapper h6 {
    font-weight: 700;
  }
  .document-wrapper img[src*="#half"],
  .document-wrapper img[src*="#twothirds"],
  .document-wrapper img[src*="#third"] {
    width: 100%;
  }
  /* On phones the bike photo (.cover-photo-mobile, inserted right after the
     name in rides.html) floats right and bleeds off-screen at the same scale
     and offset dialed in for the earlier bleed-behind version — but this
     time shape-outside wraps the entries around the bike's actual outline
     instead of sitting behind a rectangular gradient card. Since text only
     ever occupies transparent (page-white) space, no backdrop is needed for
     legibility. */
  .hero {
    flex-direction: column;
    height: auto;
    padding-bottom: 10vh;
    overflow-x: hidden; /* the float's negative margin bleeds past the edge */
  }
  .hero-right {
    position: relative;
    width: 100%;
    flex: none;
    min-height: 0;
    padding: 0;
    order: 1;
    padding-top: 88px; /* was 43px; bike and text are both inside here, so this moves both down together */
  }
  .hero-left {
    display: none;
  } /* photo moves inline on phones, see .cover-photo-mobile */
  /* display: block below (not flex) so .cover-photo-mobile can float within
     normal flow — flex items ignore float entirely, and even a flex
     container's own box avoiding a float isn't reliable across engines
     (this bit Safari/WKWebView last time). Same reasoning applies to .menu:
     .menu-icon is already display: inline-block, so a plain block .menu lets
     the icons wrap around the float as ordinary inline content instead of as
     flex items — standard, universally-supported behavior. `gap` has no
     effect outside flex/grid, so icon spacing moves to a margin instead. */
  .hero-content {
    display: block;
    padding-left: 32px;
    font-size: 1.4em; /* bump the identity text for phone readability */
  }
  .menu {
    display: block;
    margin: 23px 0px 8px -8px;
  }
  .menu-icon {
    margin: 0 3px 6px 0;
  }
  /* Same 92% width / bled-off-the-edge scale as the bleed-behind version,
     just expressed as a float (width + a negative right margin) instead of
     absolute positioning. shape-image-threshold ignores the faint
     anti-aliased edge pixels that would otherwise make the wrap line jagged;
     shape-margin gives the text breathing room off the outline. */
  .cover-photo-mobile {
    display: block;
    float: right;
    width: 92%;
    height: auto;
    margin: 4px -31% 20px 20px;
    shape-outside: url("/assets/images/covers/1300gs.webp");
    shape-image-threshold: 0.5;
    shape-margin: 16px;
  }
}

/*# sourceMappingURL=styles.css.map */