/* ==========================================================================
   Pacific Aquaculture — design system
   --------------------------------------------------------------------------
   Visual language adopted from the BloomEd front end
   (BloomEd/01. Website/01 website_front_end/src/components/*.css):
     - 135deg brand gradient as the primary surface + button fill
     - white cards, 12px radius, 0 4px 20px rgba(0,0,0,.1) shadow
     - 2px #e0e0e0 inputs with a 3px translucent focus ring
     - buttons lift translateY(-2px) on hover with a coloured glow
     - slideUp / fadeIn / pulse entrance motion
     - 700 headings, 600 labels, system-ish stack

   Recoloured to Pacific Aquaculture's own palette, read from the live Wix
   theme at www.pacific-aquaculture.com (--color_11..--color_24) so the
   colour scheme is preserved exactly rather than reinvented:
     color_18 #FE8340 action   color_12 #FCF9F4 sand    color_15 #414141 ink
     color_17 #FEB19F          color_19 #A9422B         color_24 #293E31
   Teal #217786 / #AFD0D5 are lifted from the logo artwork itself and from
   the site's own --bg-overlay-color declarations.
   ========================================================================== */

:root {
  /* --- brand: teal, straight off the two drops in the logo mark --------- */
  --pa-teal-900: #143f49;
  --pa-teal-800: #1a5a68;
  --pa-teal-700: #217786; /* primary — dark drop */
  --pa-teal-600: #2b8c9d;
  --pa-teal-500: #3d97a7;
  --pa-teal-300: #8fc2cb;
  --pa-teal-200: #afd0d5; /* light drop */
  --pa-teal-100: #d7e8eb;
  --pa-teal-50: #eff7f8;

  /* --- brand: coral / roe, the Wix action colour ramp ------------------- */
  --pa-coral-700: #a9422b; /* color_19 */
  --pa-coral-600: #d15a33;
  --pa-coral-500: #fe8340; /* color_18 — action */
  --pa-coral-300: #feb19f; /* color_17 */
  --pa-coral-200: #ffcbbf; /* color_16 */
  --pa-coral-50: #fff3ee;

  /* --- neutrals --------------------------------------------------------- */
  --pa-white: #ffffff; /* color_11 */
  --pa-sand: #fcf9f4; /* color_12 */
  --pa-line: #e0e0e0;
  --pa-line-soft: #efece7;
  --pa-ink: #414141; /* color_15 */
  --pa-ink-soft: #6b6b6a;
  --pa-ink-mute: #999997; /* color_14 */
  --pa-green-deep: #293e31; /* color_24 */

  /* --- gradients (BloomEd's 135deg formula, brand hues) ------------------ */
  --pa-grad-brand: linear-gradient(135deg, #217786 0%, #143f49 100%);
  --pa-grad-brand-rev: linear-gradient(135deg, #143f49 0%, #217786 100%);
  --pa-grad-accent: linear-gradient(135deg, #fe8340 0%, #a9422b 100%);
  --pa-grad-aqua: linear-gradient(135deg, #afd0d5 0%, #217786 100%);

  /* --- elevation (BloomEd's shadow scale) ------------------------------- */
  --pa-shadow-sm: 0 2px 10px rgba(0, 0, 0, 0.1);
  --pa-shadow-md: 0 4px 20px rgba(0, 0, 0, 0.1);
  --pa-shadow-lg: 0 10px 40px rgba(0, 0, 0, 0.15);
  --pa-glow-brand: 0 6px 20px rgba(33, 119, 134, 0.4);
  --pa-glow-accent: 0 6px 20px rgba(254, 131, 64, 0.4);

  /* --- geometry --------------------------------------------------------- */
  --pa-r-sm: 6px;
  --pa-r-md: 8px;
  --pa-r-lg: 12px;
  --pa-r-xl: 20px;
  --pa-r-pill: 999px;

  /* --- type: Raleway is the live site's own body face ------------------- */
  --pa-font: "Raleway", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;

  /* --- layout ----------------------------------------------------------- */
  --pa-max: 1140px;
  --pa-gutter: 24px;
  --pa-header-h: 76px;
}

/* ==========================================================================
   Reset & base
   ========================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
  /* the sticky header must not cover an anchored section */
  scroll-padding-top: calc(var(--pa-header-h) + 16px);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--pa-font);
  color: var(--pa-ink);
  background: var(--pa-sand);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* the crab canvas is fixed behind everything; stop rubber-banding from
     revealing the page edge on iOS */
  overscroll-behavior-y: none;
}

img {
  max-width: 100%;
  display: block;
}

/* Without this, any display: rule on an element beats the hidden attribute —
   which is how the back-to-top button stayed visible at the top of the page. */
[hidden] {
  display: none !important;
}

a {
  color: var(--pa-teal-700);
  text-decoration: none;
}

h1,
h2,
h3,
h4 {
  margin: 0 0 0.5em;
  line-height: 1.15;
  font-weight: 700;
  letter-spacing: -0.015em;
  color: var(--pa-teal-900);
}

h1 {
  font-size: clamp(2.4rem, 6vw, 4.25rem);
}
h2 {
  font-size: clamp(1.9rem, 4vw, 2.75rem);
}
h3 {
  font-size: clamp(1.15rem, 2vw, 1.4rem);
}

p {
  margin: 0 0 1rem;
}

::selection {
  background: var(--pa-teal-200);
  color: var(--pa-teal-900);
}

/* Visible focus everywhere — never remove it, only restyle it. */
:focus-visible {
  outline: 3px solid var(--pa-coral-500);
  outline-offset: 3px;
  border-radius: 4px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: 16px;
  top: -100px;
  z-index: 200;
  background: var(--pa-teal-700);
  color: #fff;
  padding: 12px 20px;
  border-radius: var(--pa-r-md);
  font-weight: 600;
  transition: top 0.2s ease;
}
.skip-link:focus {
  top: 16px;
}

/* ==========================================================================
   The 3D crab stage — fixed behind all content
   ========================================================================== */

#crab-stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* fallback tint if WebGL never starts; the canvas paints over it */
  background:
    radial-gradient(
      68% 52% at 50% 32%,
      rgba(175, 208, 213, 0.5) 0%,
      rgba(175, 208, 213, 0) 72%
    ),
    linear-gradient(180deg, #ffffff 0%, var(--pa-sand) 55%, #f4efe7 100%);
}

#crab-canvas {
  width: 100%;
  height: 100%;
  display: block;
}

/* Shown only when WebGL is unavailable or errored. */
#crab-stage[data-fallback="true"]::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("../brand/logo-round.png");
  background-repeat: no-repeat;
  background-position: center 34%;
  background-size: min(320px, 42vw);
  opacity: 0.08;
}

/* A quiet read-out of which part the crab is showing right now. */
.stage-readout {
  position: fixed;
  left: max(20px, env(safe-area-inset-left, 0px));
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  display: flex;
  flex-direction: column;
  gap: 14px;
  pointer-events: none;
}

.stage-readout li {
  display: flex;
  align-items: center;
  gap: 10px;
  list-style: none;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--pa-ink-mute);
  opacity: 0.55;
  transition: opacity 0.35s ease, color 0.35s ease;
}

.stage-readout li::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--pa-teal-200);
  transition: background 0.35s ease, transform 0.35s ease,
    box-shadow 0.35s ease;
}

.stage-readout li[aria-current="true"] {
  opacity: 1;
  color: var(--pa-teal-800);
}

.stage-readout li[aria-current="true"]::before {
  background: var(--pa-coral-500);
  transform: scale(1.6);
  box-shadow: 0 0 0 5px rgba(254, 131, 64, 0.18);
}

.stage-readout ol {
  margin: 0;
  padding: 0;
}

/* Needs a genuinely wide viewport: below this the read-out would sit on top
   of the left edge of the content column. */
@media (max-width: 1400px) {
  .stage-readout {
    display: none;
  }
}

/* ==========================================================================
   Header / navigation
   ========================================================================== */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  height: var(--pa-header-h);
  display: flex;
  align-items: center;
  padding-top: env(safe-area-inset-top, 0px);
  background: rgba(255, 255, 255, 0.72);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: background 0.3s ease, border-color 0.3s ease,
    box-shadow 0.3s ease;
}

.site-header[data-stuck="true"] {
  background: rgba(255, 255, 255, 0.93);
  border-bottom-color: var(--pa-line-soft);
  box-shadow: var(--pa-shadow-sm);
}

.header-inner {
  width: 100%;
  max-width: var(--pa-max);
  margin: 0 auto;
  padding: 0 var(--pa-gutter);
  display: flex;
  align-items: center;
  gap: 20px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-right: auto;
}

.brand img {
  height: 38px;
  width: auto;
}

.nav {
  display: flex;
  align-items: center;
  gap: 4px;
}

.nav a {
  padding: 9px 14px;
  border-radius: var(--pa-r-md);
  font-size: 14px;
  font-weight: 600;
  color: var(--pa-ink);
  transition: background 0.2s ease, color 0.2s ease;
}

.nav a:hover {
  background: var(--pa-teal-50);
  color: var(--pa-teal-700);
}

.nav a[aria-current="true"] {
  color: var(--pa-teal-700);
  background: var(--pa-teal-50);
}

.nav-toggle {
  display: none;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--pa-line);
  background: #fff;
  border-radius: var(--pa-r-md);
  cursor: pointer;
}

.nav-toggle span,
.nav-toggle span::before,
.nav-toggle span::after {
  content: "";
  display: block;
  width: 18px;
  height: 2px;
  border-radius: 2px;
  background: var(--pa-ink);
  transition: transform 0.25s ease, opacity 0.2s ease;
}
.nav-toggle span::before {
  transform: translateY(-6px);
}
.nav-toggle span::after {
  transform: translateY(4px);
}

.nav-toggle[aria-expanded="true"] span {
  background: transparent;
}
.nav-toggle[aria-expanded="true"] span::before {
  transform: rotate(45deg);
}
.nav-toggle[aria-expanded="true"] span::after {
  transform: translateY(-2px) rotate(-45deg);
}

@media (max-width: 860px) {
  .nav-toggle {
    display: flex;
  }

  .nav {
    position: absolute;
    top: calc(var(--pa-header-h) + env(safe-area-inset-top, 0px));
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    padding: 12px var(--pa-gutter) 20px;
    background: rgba(255, 255, 255, 0.98);
    border-bottom: 1px solid var(--pa-line-soft);
    box-shadow: var(--pa-shadow-md);
  }

  .nav[hidden] {
    display: none;
  }

  .nav a {
    padding: 14px;
    font-size: 16px;
  }

  .nav .btn {
    margin-top: 8px;
    justify-content: center;
  }
}

/* ==========================================================================
   Buttons — BloomEd's gradient + lift
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 14px 26px;
  border: none;
  border-radius: var(--pa-r-md);
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  text-align: center;
  transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.3s ease,
    color 0.2s ease, border-color 0.2s ease;
}

.btn-primary {
  background: var(--pa-grad-brand);
  color: #fff;
}
.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: var(--pa-glow-brand);
}

.btn-accent {
  background: var(--pa-grad-accent);
  color: #fff;
}
.btn-accent:hover {
  transform: translateY(-2px);
  box-shadow: var(--pa-glow-accent);
}

.btn-ghost {
  background: rgba(255, 255, 255, 0.8);
  color: var(--pa-teal-800);
  border: 2px solid var(--pa-teal-200);
}
.btn-ghost:hover {
  transform: translateY(-2px);
  border-color: var(--pa-teal-500);
  box-shadow: var(--pa-shadow-sm);
}

.btn:active {
  transform: translateY(0);
}

.btn-sm {
  padding: 10px 18px;
  font-size: 14px;
}

/* A disabled control must still read as a control, just an inert one. */
.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

/* ==========================================================================
   Layout primitives
   ========================================================================== */

main {
  position: relative;
  z-index: 1;
}

.section {
  position: relative;
  padding: clamp(72px, 11vh, 132px) var(--pa-gutter);
}

.wrap {
  width: 100%;
  max-width: var(--pa-max);
  margin: 0 auto;
}

.wrap-narrow {
  max-width: 760px;
}

.eyebrow {
  display: inline-block;
  margin-bottom: 14px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--pa-coral-600);
}

.lede {
  font-size: clamp(1.02rem, 1.5vw, 1.18rem);
  color: var(--pa-ink-soft);
}

/* The frosted card is what keeps body copy legible over the moving crab. */
.panel {
  background: rgba(255, 255, 255, 0.82);
  -webkit-backdrop-filter: blur(16px) saturate(160%);
  backdrop-filter: blur(16px) saturate(160%);
  border: 1px solid rgba(255, 255, 255, 0.9);
  border-radius: var(--pa-r-xl);
  box-shadow: var(--pa-shadow-md);
  padding: clamp(28px, 4vw, 52px);
}

.grid {
  display: grid;
  gap: 24px;
}

.grid-2 {
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

/* 200px, not 240px: inside .wrap-narrow three 240px tracks plus gaps overflow
   760px and the row silently breaks to 2 + 1. */
.grid-3 {
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

/* ==========================================================================
   Hero
   ========================================================================== */

.hero {
  position: relative;
  min-height: calc(100svh - var(--pa-header-h));
  display: flex;
  align-items: center;
  padding: 64px var(--pa-gutter) 96px;
}

.hero-inner {
  max-width: 620px;
}

.hero h1 {
  margin-bottom: 20px;
}

.hero h1 .accent {
  background: var(--pa-grad-accent);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.hero .lede {
  max-width: 34em;
  font-size: clamp(1.05rem, 1.7vw, 1.28rem);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 30px;
}

.hero-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 28px;
  margin-top: 38px;
  font-size: 13px;
  font-weight: 600;
  color: var(--pa-ink-soft);
}

.hero-meta span {
  display: flex;
  align-items: center;
  gap: 8px;
}

.hero-meta span::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--pa-coral-500);
}

/* Scroll prompt — the deshelling only makes sense if people scroll. */
.scroll-cue {
  position: absolute;
  left: 50%;
  bottom: 28px;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--pa-teal-700);
  opacity: 0.85;
}

.scroll-cue::after {
  content: "";
  width: 1px;
  height: 40px;
  background: linear-gradient(
    180deg,
    var(--pa-teal-500) 0%,
    rgba(33, 119, 134, 0) 100%
  );
  animation: cueDrop 2.2s ease-in-out infinite;
  transform-origin: top;
}

@keyframes cueDrop {
  0%,
  100% {
    transform: scaleY(0.3);
    opacity: 0.3;
  }
  50% {
    transform: scaleY(1);
    opacity: 1;
  }
}

/* ==========================================================================
   Cards
   ========================================================================== */

.card {
  background: #fff;
  border-radius: var(--pa-r-lg);
  box-shadow: var(--pa-shadow-md);
  padding: 30px;
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 32px rgba(20, 63, 73, 0.16);
}

.card-icon {
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  margin-bottom: 18px;
  border-radius: var(--pa-r-lg);
  background: var(--pa-grad-aqua);
  color: #fff;
}

.card h3 {
  margin-bottom: 8px;
  font-size: 1.12rem;
}

.card p {
  margin: 0;
  font-size: 0.95rem;
  color: var(--pa-ink-soft);
}

/* --- numbered process steps ------------------------------------------- */

.step {
  position: relative;
  padding-left: 62px;
}

.step-num {
  position: absolute;
  left: 0;
  top: 0;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--pa-grad-brand);
  color: #fff;
  font-weight: 700;
  font-size: 15px;
  box-shadow: 0 6px 20px rgba(33, 119, 134, 0.3);
}

/* --- stats ------------------------------------------------------------- */

.stat {
  text-align: center;
}

.stat b {
  display: block;
  font-size: clamp(2rem, 4.5vw, 3rem);
  font-weight: 700;
  line-height: 1.05;
  background: var(--pa-grad-brand);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.stat span {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--pa-ink-mute);
}

/* ==========================================================================
   Products
   ========================================================================== */

.product {
  display: flex;
  flex-direction: column;
  background: #fff;
  border-radius: var(--pa-r-lg);
  box-shadow: var(--pa-shadow-md);
  overflow: hidden;
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.product:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 32px rgba(20, 63, 73, 0.16);
}

.product-media {
  position: relative;
  aspect-ratio: 4 / 3;
  background: var(--pa-grad-aqua);
  display: grid;
  place-items: center;
}

/* A captioned photo inside a panel. */
.figure {
  margin: 0;
  border-radius: var(--pa-r-lg);
  overflow: hidden;
  background: var(--pa-teal-50);
  box-shadow: var(--pa-shadow-md);
}

.figure img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.figure figcaption {
  padding: 14px 18px;
  font-size: 0.86rem;
  color: var(--pa-ink-soft);
  background: #fff;
}

.ribbon {
  position: absolute;
  top: 12px;
  left: 12px;
  /* above the photo, which is itself positioned and comes later in the DOM */
  z-index: 1;
  padding: 5px 12px;
  border-radius: var(--pa-r-pill);
  background: var(--pa-coral-500);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.ribbon[data-tone="out"] {
  background: var(--pa-ink-mute);
}

.product-body {
  padding: 22px;
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: 6px;
}

.product-body h3 {
  margin: 0;
  font-size: 1.05rem;
}

.product-price {
  font-size: 1.3rem;
  font-weight: 700;
  color: var(--pa-teal-700);
}

.product-note {
  font-size: 0.88rem;
  color: var(--pa-ink-mute);
  margin: 0;
}

.product .btn {
  margin-top: auto;
  justify-content: center;
}

.delivery-note {
  margin-top: 26px;
  padding: 16px 20px;
  border-radius: var(--pa-r-md);
  background: var(--pa-coral-50);
  border-left: 4px solid var(--pa-coral-500);
  font-size: 0.94rem;
  color: var(--pa-coral-700);
  font-weight: 600;
}

/* ==========================================================================
   Contact
   ========================================================================== */

.contact-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 18px;
}

.contact-list a {
  font-weight: 600;
}

.contact-label {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--pa-ink-mute);
  margin-bottom: 2px;
}

/* Inputs follow BloomEd: 2px border, 8px radius, 3px translucent focus ring.
   16px font on mobile is deliberate — anything smaller makes iOS Safari zoom
   the viewport on focus, which reads as the page jumping. */
.field {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.field label {
  font-size: 14px;
  font-weight: 600;
  color: var(--pa-ink);
}

.field input,
.field textarea {
  padding: 12px 16px;
  border: 2px solid var(--pa-line);
  border-radius: var(--pa-r-md);
  font-family: inherit;
  font-size: 16px;
  color: var(--pa-ink);
  background: #fff;
  transition: border-color 0.25s ease, box-shadow 0.25s ease;
  outline: none;
}

.field textarea {
  min-height: 120px;
  resize: vertical;
}

.field input:focus,
.field textarea:focus {
  border-color: var(--pa-teal-700);
  box-shadow: 0 0 0 3px rgba(33, 119, 134, 0.12);
}

/* ==========================================================================
   Footer
   ========================================================================== */

.site-footer {
  position: relative;
  z-index: 1;
  background: var(--pa-grad-brand);
  color: rgba(255, 255, 255, 0.82);
  padding: 64px var(--pa-gutter) 32px;
  padding-bottom: calc(32px + env(safe-area-inset-bottom, 0px));
}

.site-footer h4 {
  color: #fff;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin-bottom: 14px;
}

.site-footer a {
  color: rgba(255, 255, 255, 0.82);
  transition: color 0.2s ease;
}

.site-footer a:hover {
  color: #fff;
}

.footer-logo {
  height: 46px;
  width: auto;
  margin-bottom: 16px;
  /* the wordmark is teal artwork; on the dark footer it needs to read white */
  filter: brightness(0) invert(1);
  opacity: 0.95;
}

.footer-grid {
  display: grid;
  gap: 36px;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}

.footer-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 9px;
  font-size: 0.93rem;
}

.footer-bottom {
  margin-top: 48px;
  padding-top: 22px;
  border-top: 1px solid rgba(255, 255, 255, 0.18);
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: space-between;
  font-size: 0.82rem;
  color: rgba(255, 255, 255, 0.62);
}

/* ==========================================================================
   Scroll reveal
   ========================================================================== */

[data-reveal] {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 0.6s ease-out, transform 0.6s ease-out;
}

[data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

/* ==========================================================================
   Reduced motion
   --------------------------------------------------------------------------
   A scroll-driven 3D dissection is exactly the kind of thing that triggers
   vestibular symptoms. Under this query the crab stops animating (handled in
   crab.js), reveals resolve immediately, and decorative loops stop.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  [data-reveal] {
    opacity: 1;
    transform: none;
  }

  .scroll-cue::after {
    animation: none;
    opacity: 0.6;
    transform: scaleY(1);
  }
}


/* ==========================================================================
   Video
   --------------------------------------------------------------------------
   The farm's own footage. Portrait, because that is how it was shot on a
   phone in a boat — letterboxing it into landscape would waste most of the
   frame and crop the fisherman out.
   ========================================================================== */

.video-card {
  margin: 0;
  border-radius: var(--pa-r-xl);
  overflow: hidden;
  background: #0c1614;
  box-shadow: 0 18px 44px rgba(20, 63, 73, 0.24);
  max-width: 340px;
  width: 100%;
  justify-self: end;
}

.video-card video {
  display: block;
  width: 100%;
  aspect-ratio: 9 / 16;
  object-fit: cover;
  background: #0c1614;
}

.video-card figcaption {
  padding: 12px 16px;
  font-size: 0.84rem;
  font-weight: 600;
  color: #fff;
  background: linear-gradient(180deg, rgba(12, 22, 20, 0.9), #0c1614);
}

.video-card-sm {
  max-width: 260px;
  justify-self: start;
}

.media-row {
  display: grid;
  grid-template-columns: minmax(200px, 260px) 1fr;
  gap: 32px;
  align-items: start;
  margin-top: 30px;
}

@media (max-width: 760px) {
  .media-row {
    grid-template-columns: 1fr;
  }
  .video-card,
  .video-card-sm {
    justify-self: center;
    max-width: 300px;
  }
}

/* Hero becomes two columns once there is room for the video beside the text. */
.hero-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 40px;
  align-items: center;
}

@media (min-width: 900px) {
  .hero-grid {
    grid-template-columns: 1.1fr 0.9fr;
  }
}

/* ==========================================================================
   Catalogue — grades by weight
   --------------------------------------------------------------------------
   Laid out as a price list rather than shop cards: mud crab is sold by grade,
   and a buyer scans down the weights, so the weights and prices line up in
   columns. tabular-nums keeps the dollar figures aligned.
   ========================================================================== */

.grades {
  margin-top: 30px;
  border: 1px solid var(--pa-line-soft);
  border-radius: var(--pa-r-lg);
  overflow: hidden;
  background: #fff;
  box-shadow: var(--pa-shadow-sm);
}

.grade {
  display: grid;
  grid-template-columns: 96px 1fr auto auto;
  gap: 16px;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid var(--pa-line-soft);
}

.grade:last-child {
  border-bottom: 0;
}

.grade-tag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 5px 10px;
  border-radius: var(--pa-r-pill);
  background: var(--pa-teal-50);
  color: var(--pa-teal-800);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.grade-weight {
  font-size: 0.95rem;
  color: var(--pa-ink-soft);
}

.grade-qty {
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--pa-ink-mute);
  white-space: nowrap;
}

.grade-price {
  font-size: 1.22rem;
  font-weight: 700;
  color: var(--pa-teal-800);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.grade-price small {
  font-size: 0.66em;
  font-weight: 600;
  color: var(--pa-ink-mute);
}

/* The biggest grade is the one people come for — give it the accent. */
.grade-hero {
  background: linear-gradient(90deg, var(--pa-coral-50), #fff 60%);
}

.grade-hero .grade-tag {
  background: var(--pa-grad-accent);
  color: #fff;
}

.grade-hero .grade-price {
  color: var(--pa-coral-700);
}

@media (max-width: 620px) {
  .grade {
    grid-template-columns: auto 1fr;
    grid-template-areas:
      "tag price"
      "weight qty";
    gap: 6px 14px;
    padding: 14px 16px;
  }
  .grade-tag { grid-area: tag; justify-self: start; }
  .grade-price { grid-area: price; justify-self: end; }
  .grade-weight { grid-area: weight; }
  .grade-qty { grid-area: qty; justify-self: end; }
}

/* ==========================================================================
   Dock — order and back-to-top, always within reach
   ========================================================================== */

.dock {
  position: fixed;
  right: max(18px, env(safe-area-inset-right, 0px));
  bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
}

.dock-btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  border: none;
  border-radius: var(--pa-r-pill);
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 8px 24px rgba(20, 63, 73, 0.28);
  transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.25s ease;
}

.dock-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 30px rgba(20, 63, 73, 0.34);
}

.dock-btn:active {
  transform: translateY(0);
}

/* WhatsApp green, not a brand colour — people recognise the channel by it,
   and a teal WhatsApp button reads as a generic chat bubble. */
.dock-wa {
  padding: 13px 20px;
  background: #25d366;
  color: #04301a;
}

.dock-top {
  width: 46px;
  height: 46px;
  justify-content: center;
  padding: 0;
  background: #fff;
  color: var(--pa-teal-800);
  border: 1px solid var(--pa-line-soft);
}

/* Collapse to a circle on phones, where a labelled pill eats the thumb zone. */
@media (max-width: 560px) {
  .dock-wa {
    width: 52px;
    height: 52px;
    padding: 0;
    justify-content: center;
  }
  .dock-wa span {
    display: none;
  }
}

@media print {
  .dock,
  .stage-readout,
  #crab-stage {
    display: none !important;
  }
}

/* ==========================================================================
   Photography
   --------------------------------------------------------------------------
   All of it is the farm's own. Captions say what is happening rather than
   restating the heading, because a reader who stops at a photo is usually
   asking "what am I looking at".
   ========================================================================== */

.photo {
  margin: 0;
  border-radius: var(--pa-r-lg);
  overflow: hidden;
  background: var(--pa-teal-50);
  box-shadow: var(--pa-shadow-md);
}

.photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  aspect-ratio: 3 / 4;
}

.photo-wide img {
  aspect-ratio: 4 / 3;
}

.photo figcaption {
  padding: 12px 16px;
  font-size: 0.85rem;
  color: var(--pa-ink-soft);
  background: #fff;
}

/* A row of photos that keeps one shape from end to end. */
.gallery {
  display: grid;
  gap: 20px;
  margin-top: 30px;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}

/* Image beside text, stacking on narrow screens. */
.split {
  display: grid;
  gap: 30px;
  align-items: center;
  margin-top: 32px;
  grid-template-columns: 1fr;
}

@media (min-width: 820px) {
  .split {
    grid-template-columns: 0.9fr 1.1fr;
  }
  .split-reverse > :first-child {
    order: 2;
  }
}

/* The two holding-tank clips sit side by side under the system copy. */
.tank-clips {
  display: grid;
  gap: 20px;
  margin-top: 30px;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}

.tank-clips .video-card {
  max-width: none;
  justify-self: stretch;
}

/* ==========================================================================
   Products
   --------------------------------------------------------------------------
   One card per SKU: the animal, its live weight, and what it costs. Cards
   share one shape so the range reads as a range rather than a scrapbook.
   ========================================================================== */

.sku-grid {
  display: grid;
  gap: 22px;
  margin-top: 32px;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}

.sku {
  display: flex;
  flex-direction: column;
  background: #fff;
  border-radius: var(--pa-r-lg);
  overflow: hidden;
  box-shadow: var(--pa-shadow-md);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.sku:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 32px rgba(20, 63, 73, 0.16);
}

.sku-media {
  position: relative;
  aspect-ratio: 3 / 4;
  background: var(--pa-teal-50);
  overflow: hidden;
}

.sku-media img,
.sku-media video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.sku-body {
  padding: 18px 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1;
}

.sku-name {
  margin: 0;
  font-size: 1.02rem;
  font-weight: 700;
  color: var(--pa-teal-900);
}

.sku-size {
  font-size: 0.86rem;
  color: var(--pa-ink-mute);
}

.sku-price {
  margin-top: auto;
  padding-top: 10px;
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--pa-teal-700);
  font-variant-numeric: tabular-nums;
}

/* Where there is no fixed price, say so plainly rather than leaving a gap. */
.sku-price[data-seasonal] {
  font-size: 0.95rem;
  color: var(--pa-coral-600);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* ==========================================================================
   Placeholder — a slot waiting for media, marked as such
   --------------------------------------------------------------------------
   Deliberately obvious. An empty grey box looks like a broken image; this
   says what belongs here so nobody ships it by accident.
   ========================================================================== */

.placeholder {
  display: grid;
  place-items: center;
  gap: 6px;
  padding: 28px 20px;
  min-height: 180px;
  text-align: center;
  border: 2px dashed var(--pa-teal-200);
  border-radius: var(--pa-r-lg);
  background: repeating-linear-gradient(
    45deg,
    var(--pa-teal-50),
    var(--pa-teal-50) 10px,
    #fff 10px,
    #fff 20px
  );
  color: var(--pa-teal-800);
}

.placeholder b {
  font-size: 0.92rem;
  font-weight: 700;
}

.placeholder span {
  font-size: 0.82rem;
  color: var(--pa-ink-soft);
}

.sku-media .placeholder {
  position: absolute;
  inset: 0;
  border: none;
  border-radius: 0;
  min-height: 0;
}

/* ==========================================================================
   Reviews
   --------------------------------------------------------------------------
   Real Google reviews, loaded from assets/data/reviews.json. If none are
   present the section falls back to a single card pointing at Google, which
   is honest and still useful, rather than an empty grid.
   ========================================================================== */

.review-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px 16px;
  margin-top: 6px;
}

.review-score {
  font-size: 2.4rem;
  font-weight: 700;
  line-height: 1;
  color: var(--pa-teal-800);
  font-variant-numeric: tabular-nums;
}

.stars {
  display: inline-flex;
  gap: 2px;
  color: #f5a623;
  line-height: 1;
}

.stars svg {
  width: 17px;
  height: 17px;
  display: block;
}

.stars .star-off {
  color: var(--pa-line);
}

.review-count {
  font-size: 0.9rem;
  color: var(--pa-ink-mute);
}

/* 205px lets all five sit on one row at full width instead of leaving a
   single card orphaned on a second row. */
.review-grid {
  display: grid;
  gap: 18px;
  margin-top: 28px;
  grid-template-columns: repeat(auto-fit, minmax(205px, 1fr));
  align-items: start;
}

/* All five in one row once there is room, rather than four and a lone
   orphan underneath. */
@media (min-width: 1080px) {
  .review-grid {
    grid-template-columns: repeat(5, 1fr);
    gap: 14px;
  }
  .review {
    padding: 18px 16px;
  }
  .review-text {
    -webkit-line-clamp: 8;
  }
}

.review {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 22px;
  background: #fff;
  border-radius: var(--pa-r-lg);
  box-shadow: var(--pa-shadow-md);
}

.review-top {
  display: flex;
  align-items: center;
  gap: 11px;
}

.review-avatar {
  width: 38px;
  height: 38px;
  flex: none;
  border-radius: 50%;
  object-fit: cover;
  background: var(--pa-teal-100);
}

/* No profile picture: use the initial rather than a broken image. */
.review-initial {
  width: 38px;
  height: 38px;
  flex: none;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--pa-grad-aqua);
  color: #fff;
  font-weight: 700;
  font-size: 15px;
}

.review-who {
  min-width: 0;
}

.review-author {
  display: block;
  font-weight: 700;
  font-size: 0.94rem;
  color: var(--pa-teal-900);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.review-date {
  font-size: 0.79rem;
  color: var(--pa-ink-mute);
}

/* Reviews run from one line to twenty. Left unclamped, one long review
   stretches every card beside it into dead space. Clamped, the row reads as
   one object and the full text is a click away on Google. */
.review-text {
  margin: 0;
  font-size: 0.91rem;
  color: var(--pa-ink-soft);
  display: -webkit-box;
  -webkit-line-clamp: 9;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* A photo by the same person, sitting under their words. Full card width so
   the card reads as one piece rather than text with a thumbnail stuck on. */
.review-shot {
  display: grid;
  gap: 6px;
  margin-top: 2px;
}

.review-shot img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--pa-r-md);
  display: block;
}

.review-source {
  margin-top: auto;
  padding-top: 4px;
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--pa-ink-mute);
}


/* ==========================================================================
   Graded photo with size labels
   --------------------------------------------------------------------------
   The labels are positioned as percentages of the image, so they must see the
   whole photo: `.photo-full` turns off the cropping aspect-ratio the other
   figures use. Crop this one and the labels no longer line up with the crabs.
   ========================================================================== */

.photo-full img {
  aspect-ratio: auto;
  height: auto;
}

.photo-stage {
  position: relative;
  line-height: 0;
}

.grade-pin {
  position: absolute;
  right: 4%;
  transform: translateY(-50%);
  font-size: clamp(15px, 2.6vw, 26px);
  font-weight: 800;
  letter-spacing: 0.06em;
  color: #fff;
  line-height: 1;
  /* The crate behind is mid-blue and busy, so the label needs its own
     contrast rather than relying on the photo. */
  text-shadow:
    0 2px 10px rgba(0, 0, 0, 0.75),
    0 0 3px rgba(0, 0, 0, 0.9);
  pointer-events: none;
}


/* ==========================================================================
   Customer photos
   --------------------------------------------------------------------------
   Pulled from the Google listing, so every one carries its contributor's
   name. Google requires the attribution; it also happens to be the thing
   that makes them believable.
   ========================================================================== */

.cust-photos {
  margin-top: 34px;
}

.cust-photos-title {
  font-size: 1.05rem;
  margin-bottom: 14px;
}

/* auto-fill at a fixed track, not auto-fit at 1fr: with only two or three
   leftovers, 1fr stretches each one to fill the row and they end up bigger
   than the reviews they are meant to support. */
.cust-photo-grid {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(auto-fill, 152px);
  justify-content: start;
}

@media (max-width: 560px) {
  .cust-photo-grid {
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  }
}

.cust-photo {
  position: relative;
  margin: 0;
  border-radius: var(--pa-r-md);
  overflow: hidden;
  background: var(--pa-teal-50);
  box-shadow: var(--pa-shadow-sm);
}

.cust-photo img {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
}

/* The credit sits on a gradient rather than a solid bar: these are food
   photos and a solid strip crops the plate. */
.cust-photo figcaption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 18px 10px 7px;
  font-size: 0.72rem;
  font-weight: 600;
  color: #fff;
  background: linear-gradient(180deg, rgba(10, 20, 18, 0) 0%, rgba(10, 20, 18, 0.82) 100%);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}


/* With a photo in some cards and not others, equal heights would strand the
   text ones in whitespace. Let each card size to its own content. */
.review {
  height: auto;
}


/* ==========================================================================
   System clips
   --------------------------------------------------------------------------
   Two portrait clips: side by side once there is width for it, stacked on a
   phone. Explicit columns rather than auto-fit, because auto-fit would put
   them side by side on a narrow screen too and squeeze both.
   ========================================================================== */

.system-clips {
  display: grid;
  gap: 18px;
  margin-top: 30px;
  grid-template-columns: 1fr;
}

/* Keep the portrait clips to a sensible width and centre them in their
   column. Stretched to full column width they are ~750px tall each and
   swamp the section they are meant to illustrate. */
.system-clips .video-card {
  max-width: 320px;
  justify-self: center;
  width: 100%;
}

@media (min-width: 720px) {
  .system-clips {
    grid-template-columns: 1fr 1fr;
  }
}

/* The tying clip is supplied already cropped to keep the fisherman out of
   frame (624x848). Match the frame to the footage, or `cover` crops the sides
   and throws away content that is already tightly framed. No extra crop on
   mobile: there is nothing left to hide. */
.video-card-crop video {
  aspect-ratio: 624 / 848;
}
