/* Spring Websites — converted from the original single-file design.
   Fonts self-hosted under /public/fonts; interaction states below were
   inline hover/focus styles in the source markup. */

/* latin-ext */
@font-face {
  font-family: 'Instrument Sans';
  font-style: italic;
  font-weight: 400 600;
  font-stretch: 100%;
  font-display: swap;
  src: url("/public/fonts/instrument-sans-italic-latin-ext.woff2") format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Instrument Sans';
  font-style: italic;
  font-weight: 400 600;
  font-stretch: 100%;
  font-display: swap;
  src: url("/public/fonts/instrument-sans-italic-latin.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 600;
  font-stretch: 100%;
  font-display: swap;
  src: url("/public/fonts/instrument-sans-latin-ext.woff2") format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Instrument Sans';
  font-style: normal;
  font-weight: 400 600;
  font-stretch: 100%;
  font-display: swap;
  src: url("/public/fonts/instrument-sans-latin.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/public/fonts/space-grotesk-vietnamese.woff2") format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/public/fonts/space-grotesk-latin-ext.woff2") format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/public/fonts/space-grotesk-latin.woff2") format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* scroll-padding keeps #work / #services / #process from landing underneath
   the sticky bar when the nav's in-page links are followed. */
html { scroll-behavior: smooth; scroll-padding-top: 68px; }
body { margin: 0; background: #F6F4EC; color: #122024; font-family: 'Instrument Sans', sans-serif; -webkit-font-smoothing: antialiased; }
a { color: #0E3A42; text-decoration: none; }
a:hover { color: #0E7490; }
::selection { background: #D2E6EA; }
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes eq { 0%, 100% { transform: scaleY(0.25); opacity: .7; } 50% { transform: scaleY(1); opacity: 1; } }
@keyframes tileBlock { 0%, 36% { opacity: 1; transform: scale(1); } 46%, 86% { opacity: 0; transform: scale(.92); } 96%, 100% { opacity: 1; transform: scale(1); } }
@keyframes tileSite { 0%, 36% { opacity: 0; transform: scale(.94); } 46%, 86% { opacity: 1; transform: scale(1); } 96%, 100% { opacity: 0; transform: scale(.94); } }
@keyframes lineGrow { 0%, 44% { transform: scaleX(0); } 58%, 86% { transform: scaleX(1); } 96%, 100% { transform: scaleX(0); } }
@keyframes sprout { 0%, 40% { transform: scale(0); } 56%, 88% { transform: scale(1); } 98%, 100% { transform: scale(0); } }
@keyframes floaty { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-16px); } }

/* Elements toggled by app.js carry inline display values, so the UA's
   [hidden] rule needs to outrank them. */
[hidden] { display: none !important; }
[data-chat-panel] { display: flex; }

/* ---- interaction states (were inline style-hover / style-focus) ---- */

/* home */
.hh1:hover { background:#0E7490; }
.hh2:hover { background:#0E7490;color:#F6F4EC; }
.hh3:hover { border-color:#0E7490;color:#0E7490; }
.hh4:hover { background:#12474F; }
.hh5:hover { background:#FFFFFF;color:#0E7490; }
.hh6:hover { color:#122024; }
.hf7:focus { border-color:#0E7490; }

/* contact */
.cf1:focus { border-color:#0E7490; }
.ch2:hover { background:#0E7490; }
.ch3:hover { border-color:#0E7490;color:#0E7490; }

/* guide */
.gh1:hover { border-color:#0E7490;color:#0E7490; }

/* ==========================================================================
   Responsive layout — mobile first.

   The markup is inline-styled, so any layout property that has to change with
   viewport width lives here instead of in a style="" attribute; a class can
   never beat an inline declaration. The base rules below are the phone
   layout, and min-width queries layer the wider layouts on top.

   Breakpoints: 560 (form pairs, CTA row) / 640 (prev-next, two-up process) /
   768 (main desktop shell: gutters, work + services grids, guide rows) /
   900 (full nav row, contact two-column — kept in step with initNav() in
   app.js) / 1024 (hero decoration) / 1100 (four-up process).
   ========================================================================== */

/* Centered page container. Vertical padding stays inline per section — only
   the gutters and the max-width are viewport-dependent.

   Deliberately no `width` and no `box-sizing`: the home sections are
   shrink-to-fit (auto margins inside a column flex container) and content-box,
   so their desktop width comes from their content and the padding sits outside
   the 1240px cap. Forcing either one narrows every section on desktop.
   Shrink-to-fit already clamps to the available width, so content-box cannot
   overflow a phone either. The views whose container was full-width and
   border-box to begin with declare both inline. */
.shell {
  max-width: 1240px;
  margin: 0 auto;
  padding-left: 20px;
  padding-right: 20px;
}

/* Tap feedback on the large touch targets, in the brand's accent. */
.guide-row,
.nav-links.is-open > a,
.footer-links > a { -webkit-tap-highlight-color: rgba(14,116,144,.12); }

/* ---- navigation ---------------------------------------------------------- */

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 20px;
}

.nav-brand { font-size: 22px; }

/* Hamburger: phone-only, and only rendered when there is a menu to open. */
.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin-right: -10px;
  border: none;
  background: transparent;
  color: #122024;
  cursor: pointer;
  border-radius: 10px;
}
.nav-toggle:hover { background: rgba(18,32,36,.06); }

/* Collapsed by default; opening it drops a panel under the sticky bar. */
.nav-links { display: none; font-size: 15px; font-weight: 500; }
.nav-links.is-open {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 2px;
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  padding: 8px 20px 18px;
  background: #F6F4EC;
  border-bottom: 1px solid rgba(18,32,36,.1);
  box-shadow: 0 14px 28px rgba(18,32,36,.12);
}
/* Comfortable tap targets in the dropdown. */
.nav-links.is-open > a {
  display: flex;
  align-items: center;
  min-height: 48px;
  font-size: 17px;
  border-bottom: 1px solid rgba(18,32,36,.07);
}
.nav-links.is-open > button {
  justify-content: center;
  margin-top: 14px;
  min-height: 48px;
}

/* ---- hero ---------------------------------------------------------------- */

/* Purely decorative animated tile cluster: 410px wide and absolutely
   positioned, so it has nowhere to go on a phone. It carries no content and
   is pointer-events:none, so hiding it costs nothing. */
.hero-deco { display: none; }

/* Tight display leading works on one or two wide lines; wrapped over five
   lines on a phone it closes up, so it only tightens from 768px. */
.hero-title { line-height: 1.09; }

.hero-lede {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 28px;
  margin-top: 32px;
  position: relative;
  z-index: 1;
}
.hero-cta { display: flex; flex-direction: column; gap: 12px; }
.hero-cta > a,
.hero-cta > button { justify-content: center; }

/* Industry marquee: the desktop size scrolls too fast to read on a phone. */
.marquee { font-size: 15px; gap: 32px; }

/* ---- section heads (title on one line, supporting copy under) ------------ */

/* Bottom margin stays inline: it differs per section, and a clamp() covers
   the mobile reduction without needing a breakpoint. */
.sec-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
}

/* ---- grids --------------------------------------------------------------- */

.grid-work { display: grid; grid-template-columns: 1fr; gap: 36px; }
.grid-services { display: grid; grid-template-columns: 1fr; gap: 1px; }
.grid-process { display: grid; grid-template-columns: 1fr; gap: 24px; }
.service-cell { padding: 28px 22px; }

/* The flip card's height comes from the front face's 1568/720 aspect ratio,
   which on a narrow screen is shorter than the service list on the back. The
   tallest back face measures 267px at the narrowest card width, so this floor
   keeps every card's services readable instead of trapping them in a scroll
   box.

   It is deliberately not reset at a breakpoint: what matters is the card's own
   width, not the viewport's. The grid goes two-up at 768px, which makes the
   cards narrow — and so short — all over again, right up to ~1264px. Above
   that the aspect ratio alone clears this and the floor is inert.

   The front face has to be stretched to match. It is a plain block whose height
   comes from the chrome bar plus the screenshot's aspect ratio, while the back
   is absolutely positioned and fills the box — so without the flex rule below
   the white card ends ~70px short and leaves a transparent gap above the
   caption. Stretching the front rather than the image keeps the screenshot at
   its own 1568/720 ratio, uncropped, with the remainder as card interior. */
.card-flip { min-height: 272px; }
.card-flip > [data-flip-inner] {
  min-height: inherit;
  display: flex;
  flex-direction: column;
}
.card-flip > [data-flip-inner] > :first-child { flex: 1; }

.card-meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}

/* ---- guide rows ---------------------------------------------------------- */

/* Phone: index + title on the first line, category and date on the second.
   Desktop restores the original single-line four-column layout. */
.guide-row {
  display: grid;
  grid-template-columns: 30px auto 1fr;
  align-items: baseline;
  gap: 10px 12px;
  padding: 20px 4px;
}
.guide-row > :nth-child(1) { grid-column: 1; grid-row: 1; }
.guide-row > :nth-child(2) { grid-column: 2 / -1; grid-row: 1; }
.guide-row > :nth-child(3) { grid-column: 2; grid-row: 2; justify-self: start; }
/* white-space: the date ships nowrap, which pushes the meta line's minimum
   past a 320px screen when the category pill is long. Wrapping only kicks in
   when it actually has to. */
.guide-row > :nth-child(4) {
  grid-column: 3;
  grid-row: 2;
  justify-self: start;
  white-space: normal;
}

/* ---- guides index -------------------------------------------------------- */

.guides-head {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 22px;
  margin-bottom: 32px;
}
.guides-search-wrap {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
}
.guides-search { width: 100%; box-sizing: border-box; }

/* ---- guide article ------------------------------------------------------- */

.prevnext { display: grid; grid-template-columns: 1fr; gap: 14px; }
/* The cards pin themselves to columns 1 and 2; left alone, column 2 would
   survive the collapse to a single column as an implicit track. */
.prevnext > a { grid-column: 1 !important; text-align: left !important; }

/* ---- contact ------------------------------------------------------------- */

.contact-grid { display: grid; grid-template-columns: 1fr; gap: 40px; align-items: start; }
.contact-card { padding: 24px 20px; }
.form-pair { display: grid; grid-template-columns: 1fr; gap: 18px; }
/* iOS zooms the whole page in when a focused control's text is under 16px.
   Restored to the design's sizes at 768px in the desktop block below. */
.contact-card input,
.contact-card select,
.contact-card textarea,
[data-chat-input],
.guides-search input { font-size: 16px !important; }
.contact-card button[type="submit"] { align-self: stretch; }

/* ---- closing CTA --------------------------------------------------------- */

/* "hello@springwebsites.com" is a single unbreakable ~190px string, which set
   the whole card's minimum width above a 320px screen. Stacked, centred, with
   trimmed padding and a permitted break, it fits. */
.cta-actions {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
}
.cta-actions > a {
  justify-content: center;
  text-align: center;
  padding: 15px 18px;
  overflow-wrap: anywhere;
}

/* ---- chat panel ---------------------------------------------------------- */

.chat-panel { width: 100%; }
.chat-panel [data-chat-send] { flex-shrink: 0; }

/* ---- footer -------------------------------------------------------------- */

.footer-inner {
  max-width: 1240px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 20px;
  padding: 28px 20px;
}
.footer-links { display: flex; flex-wrap: wrap; gap: 8px 22px; font-size: 15px; }
/* Taller hit areas for thumbs; reverted at 768px so the footer keeps its
   original height on desktop. */
.footer-links > a { display: inline-flex; align-items: center; min-height: 34px; }

/* ==========================  560px and up  ================================ */
@media (min-width: 560px) {
  .cta-actions {
    flex-direction: row;
    justify-content: center;
    flex-wrap: wrap;
    gap: 14px;
  }
  .cta-actions > a { padding: 17px 34px; overflow-wrap: normal; }

  .form-pair { grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 20px; }
  .contact-card button[type="submit"] { align-self: flex-start; }
  .hero-cta { flex-direction: row; }
  .hero-cta > a,
  .hero-cta > button { justify-content: flex-start; }
}

/* ==========================  640px and up  ================================ */
@media (min-width: 640px) {
  .grid-process { grid-template-columns: repeat(2, 1fr); gap: 32px; }
  .prevnext { grid-template-columns: 1fr 1fr; gap: 20px; }
  .prevnext > a:first-child { grid-column: 1 !important; }
  .prevnext > a.is-next { grid-column: 2 !important; text-align: right !important; }
}

/* ==========================  768px and up  ================================ */
@media (min-width: 768px) {
  .shell { padding-left: 48px; padding-right: 48px; }

  /* Gutters match the page shell here, but the menu stays collapsed: the full
     row (brand + five links + CTA) does not fit until 900px. */
  .nav { padding: 14px 48px; gap: 24px; }
  .nav-links.is-open { padding: 8px 48px 18px; }

  .hero-title { line-height: 1.02; }

  .hero-lede {
    flex-direction: row;
    justify-content: space-between;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: 48px;
    margin-top: 48px;
  }
  .marquee { font-size: 20px; gap: 56px; }

  .sec-head {
    flex-direction: row;
    justify-content: space-between;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: 32px;
  }

  .grid-work { grid-template-columns: repeat(2, 1fr); gap: 48px 36px; }
  .grid-services { grid-template-columns: repeat(2, 1fr); }
  .service-cell { padding: 40px; }

  .guide-row {
    grid-template-columns: 64px 1fr auto auto;
    align-items: center;
    gap: 28px;
    padding: 26px 12px;
  }
  .guide-row > :nth-child(1) { grid-column: 1; grid-row: 1; }
  .guide-row > :nth-child(2) { grid-column: 2; grid-row: 1; }
  .guide-row > :nth-child(3) { grid-column: 3; grid-row: 1; }
  .guide-row > :nth-child(4) {
    grid-column: 4;
    grid-row: 1;
    justify-self: end;
    text-align: right;
    min-width: 150px;
    white-space: nowrap;
  }

  .guides-head {
    flex-direction: row;
    justify-content: space-between;
    align-items: flex-end;
    gap: 32px;
    margin-bottom: 44px;
  }
  .guides-search-wrap { align-items: flex-end; }
  .guides-search { width: 320px; }

  .contact-card { padding: 40px; }
  .chat-panel { width: min(440px, 100vw); }

  /* Back to the design's control sizes — the iOS zoom guard is phone-only. */
  .contact-card input,
  .contact-card select,
  .contact-card textarea,
  .guides-search input { font-size: 15px !important; }
  [data-chat-input] { font-size: 14.5px !important; }

  .footer-inner {
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 24px;
    padding: 32px 48px;
  }
  .footer-links { gap: 28px; font-size: 14px; }
  .footer-links > a { display: inline; min-height: 0; }
}

/* ==========================  900px and up  ================================ */
@media (min-width: 900px) {
  .contact-grid {
    grid-template-columns: minmax(300px, 1fr) minmax(420px, 1.3fr);
    gap: 72px;
  }

  html { scroll-padding-top: 88px; }

  /* The full nav row fits from here — drop the hamburger. */
  .nav { padding: 18px 48px; }
  .nav-brand { font-size: 29px; }
  .nav-toggle { display: none; }
  .nav-links,
  .nav-links.is-open {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 36px;
    position: static;
    padding: 0;
    background: none;
    border-bottom: none;
    box-shadow: none;
  }
  .nav-links.is-open > a {
    display: inline;
    min-height: 0;
    font-size: 15px;
    border-bottom: none;
  }
  .nav-links.is-open > button { margin-top: 0; min-height: 0; }
}

/* =========================  1024px and up  ================================ */
@media (min-width: 1024px) {
  .hero-deco { display: block; }
}

/* =========================  1100px and up  ================================ */
@media (min-width: 1100px) {
  .grid-process { grid-template-columns: repeat(4, 1fr); gap: 36px; }
}

/* Respect the OS "reduce motion" setting — the page runs several infinite
   animations, and the marquee in particular is a vestibular trigger. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  /* .hero-deco itself carries the float animation, so it needs naming
     alongside its descendants. */
  .marquee,
  .hero-deco,
  .hero-deco * { animation: none !important; }
  [data-flip-inner] { transition: none !important; }
}
