/* KG's & INCH's - page-level blocks */

/* ---------- banner slider ---------- */

.slider {
  position: relative;
  overflow: hidden;
  background: var(--navy-deep);
}

.slides { position: relative; }

/* A slide is the picture. The wording is inside the artwork, so there is no
   copy layer and nothing to keep legible over it - which also means no scrim,
   no text shadow, and no crop that could cut a headline in half. */
.slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity .6s var(--ease);
}
.slide:first-child { position: relative; }
.slide.is-active { opacity: 1; visibility: visible; z-index: 2; }

.slide-img { display: block; }
.slide-img img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* The first slide is in the flow and sets the height of the band; the rest are
   stacked on top of it. The floor stops one tall upload from making the banner
   half the screen, the ceiling stops a wide one from becoming a sliver. */
.slide:first-child .slide-img img {
  min-height: clamp(240px, 26vw, 380px);
  max-height: 62vh;
}

/* The artwork already carries a navy-to-green panel down its left side - it
   was painted to hold wording, back when the wording was part of the picture.
   The words are HTML now, but the panel is still there, so this layer only has
   to make up the difference.

   It used to lay a second 68% navy wash over that panel. Two darkenings on top
   of each other read as a flat blue block with a hard edge where the artwork's
   own fade ends, which is not a gradient anyone designed - it is just two of
   them added up. What is left here is a light lift for contrast at the bottom,
   where the green of the panel is palest and white text needs the help. */
/* The darkening is its own full-bleed layer, not the container's background.
   .slide-inner is a .container - 1200px wide and centred - so painting the
   gradient on it stopped 83px short of each edge on a 1366px screen and drew a
   hard vertical line down both sides of the banner. The scrim has to span the
   picture; only the words belong inside the container. */
.slide::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(180deg, rgba(17, 44, 80, 0) 45%, rgba(17, 44, 80, .42) 100%),
    linear-gradient(90deg, rgba(17, 44, 80, .22) 0%, rgba(17, 44, 80, .04) 38%, rgba(17, 44, 80, 0) 55%);
  pointer-events: none;
}

.slide-inner {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: flex-end;
  padding-block: var(--sp-7);
  pointer-events: none;
}

/* Held to about 34rem: a headline that runs the full width of a 1600px banner
   is read in one sweep of the eyes and lands as a slogan, not a sentence. */
.slide-copy {
  max-width: 34rem;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-4);
}

.slide-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.75rem, 3.6vw, 3rem);
  line-height: 1.12;
  letter-spacing: -0.01em;
  color: #fff;
  text-wrap: balance;
  text-shadow: 0 2px 18px rgba(10, 26, 48, .45);
}

.slide-sub {
  margin: 0;
  font-size: clamp(0.95rem, 1.35vw, 1.125rem);
  line-height: 1.6;
  color: rgba(255, 255, 255, .92);
  text-shadow: 0 1px 12px rgba(10, 26, 48, .45);
}

.slide-cta { pointer-events: auto; }

/* Up to 900px, not 700. The artwork's panel covers the left half of the
   picture, and by about 700px the headline has stopped fitting inside it and is
   running onto the bright food - which is exactly where white text disappears.
   Below this width the picture goes behind the words instead of beside them,
   the dark wash goes edge to edge, and the copy takes the full width. */
@media (max-width: 900px) {
  .slide::after {
    background: linear-gradient(180deg, rgba(17, 44, 80, .2) 0%, rgba(17, 44, 80, .82) 65%);
  }
  .slide-inner { padding-block: var(--sp-6); }
  .slide-copy { max-width: none; gap: var(--sp-3); }
  .slide-cta { width: 100%; justify-content: center; }
}
.slider-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 5;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1.5px solid rgba(255, 255, 255, .45);
  background: rgba(17, 44, 80, .38);
  backdrop-filter: blur(6px);
  color: #fff;
  cursor: pointer;
  transition: background-color .18s var(--ease), border-color .18s var(--ease);
}
.slider-arrow:hover { background: var(--green); border-color: var(--green); }
.slider-arrow.prev { left: var(--sp-4); }
.slider-arrow.next { right: var(--sp-4); }
/* Hidden at the same width the copy goes full-bleed. Left at 760px they sat
   on top of a headline that now runs the whole banner, and the left arrow ate
   the first word of it. The dots below still change slides. */
@media (max-width: 900px) { .slider-arrow { display: none; } }

.slider-dots {
  position: absolute;
  left: 50%;
  /* less the padding the buttons carry, so the rules sit where they did */
  bottom: calc(var(--sp-5) - 20px);
  transform: translateX(-50%);
  z-index: 5;
  display: flex;
  gap: 0;
}
/* The dot itself is a 4px rule, far too fine to hit with a thumb. The button
   is padded out and paints the rule through background-clip, so the mark stays
   thin while the tap area does not. 44px tall is the size a fingertip actually
   needs, so the button is a full 44x44 while the rule it paints stays 30px
   wide and 4px tall - exactly as it looked before. The gap is 0 because each
   button now carries its own 7px of side padding. */
.slider-dots button {
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 2px;
  background: rgba(255, 255, 255, .45);
  background-clip: content-box;
  padding: 20px 7px;
  cursor: pointer;
  transition: background-color .2s var(--ease), width .2s var(--ease);
}
.slider-dots button.is-active { background: var(--green); background-clip: content-box; width: 60px; }

/* Text links that sit on their own line - "Forgotten your password?", the
   footer credit - are only as tall as their text, which is around 17px and
   awkward to hit on a phone. Padding gives them a thumb-sized target without
   moving anything on the page. */
.form-note a,
.footer-bottom a,
.account-links a { display: inline-block; }

@media (hover: none) {
  .form-note a,
  .footer-bottom a { padding-block: var(--sp-2); }
}

/* ---------- about short ---------- */

.about-short {
  display: grid;
  grid-template-columns: .85fr 1.15fr;
  gap: clamp(var(--sp-6), 5vw, var(--sp-8));
  align-items: center;
}
@media (max-width: 860px) {
  .about-short { grid-template-columns: 1fr; }
}

.about-figure {
  position: relative;
  margin: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  aspect-ratio: 4 / 5;
  box-shadow: var(--shadow-lg);
}
.about-figure img { width: 100%; height: 100%; object-fit: cover; object-position: center 22%; }

/* Caption sits over the foot of the photo, on a scrim so it stays readable
   whatever the picture is doing behind it. */
.about-figure figcaption {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--sp-6) var(--sp-5) var(--sp-4);
  background: linear-gradient(180deg, rgba(17,44,80,0) 0%, rgba(17,44,80,.82) 62%, rgba(17,44,80,.92) 100%);
  color: #fff;
}
.about-figure figcaption strong {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 700;
  line-height: 1.1;
}
.about-figure figcaption span {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #9FD46A;
}

.about-stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-7);
  margin: 0;
  padding-block: var(--sp-5);
  border-block: 1px solid var(--line);
}
.about-stats div { display: flex; flex-direction: column; gap: 2px; }
.about-stats dt {
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: 700;
  color: var(--green-deep);
  line-height: 1;
}
.about-stats dd {
  margin: 0;
  font-size: var(--step--1);
  color: var(--muted);
  letter-spacing: .08em;
  text-transform: uppercase;
  font-weight: 600;
}

/* ---------- the promise, over the banner's edge ---------- */

/* "No Powder / No Pills / No Starvation" - three lines the clinic supplied,
   as three white cards pulled up over the bottom of the banner so the
   picture and the promise read as one piece. It is the one part of the page
   that is a stance rather than a fact, and the overlap is what gives it a
   place of its own without a heading over it. Three across on a desktop;
   on a phone the cards stack, each turned sideways (icon left, words right)
   so three of them are still one short screen. */

.slider, .promise-band { --promise-overlap: 2.5rem; }

/* The banner's copy and dots are bottom-aligned; when the cards follow, both
   are lifted by the overlap so the button and the dots keep their clearance
   from the edge instead of being sat on. */
@media (min-width: 701px) {
  .slider:has(+ .promise-band) .slide-inner { padding-bottom: calc(var(--sp-7) + var(--promise-overlap)); }
  .slider:has(+ .promise-band) .slider-dots { bottom: calc(var(--promise-overlap) + var(--sp-2) - 20px); }
}

.promise-band {
  position: relative;
  z-index: 6;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  padding-bottom: var(--sp-5);
}

.promise-grid {
  list-style: none;
  margin: calc(var(--promise-overlap) * -1) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-5);
}

.promise-card {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  transition: transform .22s var(--ease), box-shadow .22s var(--ease);
}
.promise-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
}

/* The icon sits in a soft green disc; the "no" badge on its shoulder is the
   only red on the page, and small on purpose - the line is a promise, not a
   warning. */
.promise-icon {
  position: relative;
  flex: none;
  width: 64px;
  height: 64px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--green-soft);
  color: var(--green-deep);
}
.promise-icon > svg {
  width: 34px;
  height: 34px;
}
.promise-no {
  position: absolute;
  right: -4px;
  top: -4px;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--surface);
  color: var(--danger);
  box-shadow: 0 1px 3px rgba(26, 66, 120, .18);
}
.promise-no svg { width: 18px; height: 18px; }

.promise-text {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  min-width: 0;
}
.promise-text strong {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-1);
  line-height: 1.2;
  color: var(--navy);
  letter-spacing: -0.005em;
}
.promise-text small {
  font-size: var(--step--1);
  line-height: 1.45;
  color: var(--ink-2);
}

@media (max-width: 900px) {
  .promise-grid { gap: var(--sp-4); }
  .promise-card { padding: var(--sp-4); gap: var(--sp-3); }
  .promise-icon { width: 54px; height: 54px; }
  .promise-icon > svg { width: 28px; height: 28px; }
}

@media (max-width: 700px) {
  /* No overlap on a phone: the banner is short and its copy already sits
     tight to the bottom, so the cards start under it and the dots are left
     alone. Lifting the copy pushed the heading under the sticky header. */
  .slider, .promise-band { --promise-overlap: 0rem; }
  .promise-band { padding-top: var(--sp-4); padding-bottom: var(--sp-4); }
  .promise-grid {
    grid-template-columns: 1fr;
    gap: var(--sp-3);
  }
  .promise-card { padding: var(--sp-3) var(--sp-4); }
  .promise-card:hover { transform: none; }
  .promise-icon { width: 48px; height: 48px; }
  .promise-icon > svg { width: 26px; height: 26px; }
  .promise-no { width: 22px; height: 22px; right: -3px; top: -3px; }
  .promise-no svg { width: 15px; height: 15px; }
}

/* ---------- the four figures, under the banner ---------- */

/* A band rather than a row inside a column, so the numbers are the first thing
   below the picture and are read before any prose. Four across on a desktop,
   two-by-two on a phone - never one long column, which turns four facts into
   four screens. */

.stat-band {
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.about-stats.stat-bar {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--sp-5);
  padding-block: var(--sp-6);
  border-block: 0;
  text-align: center;
}
.about-stats.stat-bar div {
  align-items: center;
  gap: var(--sp-2);
  position: relative;
}

/* Hairlines between the figures, not around them. */
.about-stats.stat-bar div + div::before {
  content: "";
  position: absolute;
  left: calc(var(--sp-5) / -2);
  top: 10%;
  bottom: 10%;
  border-left: 1px solid var(--line);
}

.about-stats.stat-bar dt {
  font-size: var(--step-4);
  color: var(--navy);
  /* The figures are the one place tabular digits matter - the count-up in
     main.js re-renders them every frame and proportional digits jitter. */
  font-variant-numeric: tabular-nums;
}
.about-stats.stat-bar dd { color: var(--ink-2); }

@media (max-width: 860px) {
  .about-stats.stat-bar { grid-template-columns: repeat(2, 1fr); gap: var(--sp-6) var(--sp-4); }
  .about-stats.stat-bar dt { font-size: var(--step-3); }
  /* The rule between columns 1 and 2 stays; the one that would land at the
     start of a wrapped row does not. */
  .about-stats.stat-bar div:nth-child(odd)::before { display: none; }
}

/* ---------- 5 minutes free call ---------- */

.free-call { background: var(--green-soft); }

.free-call-inner {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: var(--sp-8);
  align-items: center;
}

.free-call-points { margin: 0; }

.free-call-action .aside-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  background: var(--surface);
}

.free-call-mark {
  display: grid;
  place-items: center;
  width: 62px;
  height: 62px;
  border-radius: 50%;
  background: var(--green-soft);
  border: 1px solid var(--green);
  color: var(--green-deep);
}

.free-call-label {
  margin: 0;
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The readable number. On a desktop the tel: link does nothing, so this is
   what the visitor actually uses - it has to be large enough to copy by eye. */
.free-call-number {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 700;
  color: var(--navy);
  text-decoration: none;
  letter-spacing: .01em;
}
.free-call-number:hover { color: var(--green-deep); }

.free-call .btn .icon { vertical-align: -3px; margin-right: 6px; }

@media (max-width: 860px) {
  .free-call-inner { grid-template-columns: 1fr; gap: var(--sp-6); }
}

/* ---------- the sticky call button, phones only ---------- */

/* Hidden above 860px: on a desktop it would cover the page to offer a link
   that cannot dial anything. */
.call-fab { display: none; }

@media (max-width: 860px) {
  .call-fab {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    position: fixed;
    right: var(--sp-4);
    bottom: var(--sp-4);
    z-index: 60;
    padding: var(--sp-3) var(--sp-5);
    border-radius: var(--radius-pill);
    background: var(--green);
    color: #fff;
    font-weight: 700;
    font-size: var(--step--1);
    text-decoration: none;
    box-shadow: var(--shadow-lg);
  }
  .call-fab:hover, .call-fab:focus-visible { background: var(--green-deep); color: #fff; }
}

/* ---------- cards on the home page ---------- */

.link-more {
  font-size: var(--step--1);
  font-weight: 700;
  color: var(--green-deep);
  letter-spacing: .01em;
}

.program-card .card-media { aspect-ratio: 4 / 3; }



.recipe-card .card-media { aspect-ratio: 1 / 1; position: relative; }

.recipe-card time, .blog-card time {
  font-size: var(--step--1);
  color: var(--muted);
  font-weight: 600;
  letter-spacing: .04em;
}

.media-tag {
  position: absolute;
  left: var(--sp-3);
  top: var(--sp-3);
  background: rgba(255, 255, 255, .94);
  color: var(--green-deep);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  padding: 0.3rem 0.55rem;
  border-radius: var(--radius-sm);
}

.blog-card .card-media { aspect-ratio: 16 / 10; }


/* ---------- testimonials ---------- */

.testimonial {
  display: flex;
  flex-direction: column;
  margin: 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  transition: box-shadow .2s var(--ease), transform .2s var(--ease);
}
.testimonial:hover { box-shadow: var(--shadow); transform: translateY(-3px); }

.testimonial-photo { aspect-ratio: 4 / 3; background: var(--surface-2); }
.testimonial-photo img { width: 100%; height: 100%; object-fit: cover; }

.testimonial-body {
  padding: var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.result-tag {
  align-self: flex-start;
  background: var(--green-soft);
  color: var(--green-deep);
  font-size: var(--step--1);
  font-weight: 700;
  padding: 0.3rem 0.7rem;
  border-radius: var(--radius-pill);
}

.testimonial blockquote {
  margin: 0;
  color: var(--ink-2);
  font-size: var(--step-0);
  line-height: 1.6;
}
.testimonial figcaption {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  font-weight: 700;
  color: var(--navy);
  font-size: var(--step--1);
}

/* Most of the stories were written before anyone thought to ask for a photo.
   Those cards close up rather than holding open an empty picture frame, and
   the initial rides beside the name instead. */
.testimonial-initial {
  flex: 0 0 34px;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-family: var(--font-display);
  font-size: var(--step-0);
  font-weight: 700;
  color: var(--green-deep);
  background: var(--green-soft);
}

/* Without a photograph the quote is the whole card, so it is given the weight
   the picture would have carried. */
.testimonial.no-photo blockquote { font-size: var(--step-0); color: var(--ink); }


/* ---------- testimonials page ---------- */

/* The home strip clips each story to a line and a half because it is six
   across. This page prints the whole thing, and the stories run from two lines
   to thirty - so it is columns, not a grid. A grid would hold every row to the
   height of its tallest card and leave a half page of white under the rest. */
.story-grid {
  columns: 3;
  column-gap: var(--sp-5);
}
.story-grid > * {
  break-inside: avoid;
  /* Safari still reads the old name, and a story split across two columns is
     unreadable rather than merely untidy. */
  -webkit-column-break-inside: avoid;
  page-break-inside: avoid;
  margin-bottom: var(--sp-5);
}

/* The photographs are before-and-after posts with the weights written across
   them, and they arrive both square and 4:5. Any fixed frame crops the numbers
   off one shape or the other, so here they keep their own - which columns can
   absorb and a grid could not. */
.story-grid .testimonial-photo { aspect-ratio: auto; }
.story-grid .testimonial-photo img { height: auto; }
.story-grid .testimonial blockquote { font-size: var(--step--1); }

@media (max-width: 1024px) { .story-grid { columns: 2; } }
@media (max-width: 680px)  { .story-grid { columns: 1; } }

/* ---------- page hero (inner pages) ---------- */

.page-hero {
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
  padding-block: var(--sp-7);
}
/* Smaller than the global h1: an inner page opens with a line of orientation,
   not a poster, and at the full display size a heading like "Nutrition that is
   measured, not guessed" ran most of the width of the screen. */
.page-hero h1 {
  color: var(--navy);
  font-size: clamp(1.6rem, 2.9vw, 2.375rem);
}
.page-hero .lede { color: var(--ink-2); font-size: var(--step-1); max-width: 62ch; }

/* ---------- value cards (about) ---------- */

/* ---------- what we hold to ---------- */

.value-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-5);
  overflow: hidden;
  transition: transform .25s var(--ease), box-shadow .25s var(--ease),
              border-color .25s var(--ease);
}
/* The green rail is painted rather than bordered so it can run the width of
   the card on hover. */
.value-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  height: 3px;
  width: 3.5rem;
  background: var(--green);
  transition: width .35s var(--ease);
}
.value-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow);
  border-color: var(--line-strong);
}
.value-card:hover::before { width: 100%; }

.value-icon {
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  margin-top: var(--sp-2);
  border-radius: var(--radius);
  background: var(--green-soft);
  color: var(--green-deep);
  transition: background-color .25s var(--ease), color .25s var(--ease);
}
.value-card:hover .value-icon { background: var(--green); color: #fff; }

.value-card h3 { font-size: var(--step-1); color: var(--navy); }
.value-card p { color: var(--ink-2); font-size: var(--step--1); }

/* Each icon moves in the way its own idea moves: the needle sweeps, the bowl
   steams, the slash is drawn through the capsule, the week comes round. */
.icon [class^="ic-"] {
  transform-box: view-box;
  transform-origin: center;
}

.icon-gauge .ic-needle {
  transform-origin: 12px 18px;
  transition: transform .5s cubic-bezier(.34, 1.3, .5, 1);
}
.value-card:hover .icon-gauge .ic-needle { transform: rotate(-34deg); }

.icon-bowl .ic-steam {
  opacity: 0;
  transform: translateY(2px);
  transition: opacity .3s var(--ease), transform .45s var(--ease);
}
.value-card:hover .icon-bowl .ic-steam { opacity: 1; transform: translateY(0); }

.icon-no-pill .ic-slash {
  stroke-dasharray: 21;
  stroke-dashoffset: 21;
  transition: stroke-dashoffset .45s var(--ease);
}
.value-card:hover .icon-no-pill .ic-slash { stroke-dashoffset: 0; }

.icon-refresh-week .ic-spin {
  transform-origin: 12.3px 15.1px;
  transition: transform .6s var(--ease);
}
.value-card:hover .icon-refresh-week .ic-spin { transform: rotate(360deg); }

@media (prefers-reduced-motion: reduce) {
  .value-card,
  .value-card::before,
  .value-icon,
  .icon [class^="ic-"] { transition: none; }
  .value-card:hover { transform: none; }
  .icon-bowl .ic-steam { opacity: 1; transform: none; }
  .icon-no-pill .ic-slash { stroke-dashoffset: 0; }
}

/* ---------- clinic card ---------- */

.clinic-card {
  display: flex;
  flex-direction: column;
  gap: 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--green);
  border-radius: var(--radius);
  padding: var(--sp-5);
}
.clinic-code {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}
.clinic-card h3 { font-size: var(--step-1); color: var(--navy); }
.clinic-card p { color: var(--ink-2); font-size: var(--step--1); }

/* ---------- timeline (how it works) ---------- */

.timeline {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: 780px;
  display: flex;
  flex-direction: column;
}

.tl-item {
  position: relative;
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: var(--sp-5);
  padding-bottom: var(--sp-6);
}
/* connector between the numbers */
.tl-item::before {
  content: "";
  position: absolute;
  left: 27px;
  top: 52px;
  bottom: -4px;
  width: 2px;
  background: var(--line-strong);
}
.tl-item:last-child { padding-bottom: 0; }
.tl-item:last-child::before { display: none; }

.tl-num {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--green-soft);
  border: 2px solid var(--green);
  color: var(--green-deep);
  font-family: var(--font-mono);
  font-size: var(--step-0);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.tl-body { padding-top: var(--sp-2); display: flex; flex-direction: column; gap: var(--sp-2); }
.tl-body h3 { font-size: var(--step-2); color: var(--navy); }
.tl-body p { color: var(--ink-2); }

/* ---------- program detail ---------- */

.program-detail {
  display: grid;
  grid-template-columns: 1fr 340px;
  gap: clamp(var(--sp-5), 4vw, var(--sp-7));
  align-items: start;
}
@media (max-width: 900px) { .program-detail { grid-template-columns: 1fr; } }

.program-hero-img {
  width: 100%;
  border-radius: var(--radius-lg);
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

.program-aside { display: flex; flex-direction: column; gap: var(--sp-4); position: sticky; top: 110px; }
@media (max-width: 900px) { .program-aside { position: static; } }

.aside-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}
.aside-card h3 { font-size: var(--step-1); color: var(--navy); }
.aside-card p { color: var(--ink-2); font-size: var(--step--1); }
.aside-links { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.aside-links a { color: var(--ink-2); font-size: var(--step--1); }
.aside-links a:hover { color: var(--green-deep); }

.tick-list { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--sp-3); }
.tick-list li {
  position: relative;
  padding-left: var(--sp-6);
  color: var(--ink-2);
}
.tick-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.45em;
  width: 16px;
  height: 9px;
  border-left: 2.5px solid var(--green);
  border-bottom: 2.5px solid var(--green);
  transform: rotate(-45deg);
}

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

.contact-grid {
  display: grid;
  grid-template-columns: 1fr 1.05fr;
  gap: clamp(var(--sp-5), 4vw, var(--sp-7));
  align-items: start;
}
@media (max-width: 900px) { .contact-grid { grid-template-columns: 1fr; } }

.contact-rows { display: flex; flex-direction: column; gap: var(--sp-3); }

.contact-row {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-4) var(--sp-5);
  color: var(--ink);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
.contact-row:hover { text-decoration: none; border-color: var(--green); box-shadow: var(--shadow-sm); }
/* The e-mail address is one unbreakable word about 180px wide, which on a
   320px screen was enough to hold the whole column open. min-width lets the
   text block shrink and break-word lets the address itself wrap.

   break-word rather than anywhere: the view offers a break before the @, and
   anywhere ignores that and splits at the last character that fits. With
   break-word the offered break is taken first, and a still-too-long word
   falls back to breaking wherever it must. */
.contact-row > span:last-child {
  display: flex; flex-direction: column;
  min-width: 0;
  overflow-wrap: break-word;
}

.contact-icon {
  display: grid;
  place-items: center;
  width: 42px; height: 42px;
  border-radius: 50%;
  background: var(--green-soft);
  color: var(--green-deep);
  flex: none;
}
.contact-label {
  font-size: var(--step--1);
  color: var(--muted);
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.contact-value { font-weight: 700; color: var(--navy); }

.contact-form-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: clamp(var(--sp-5), 3vw, var(--sp-6));
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  box-shadow: var(--shadow);
}

/* ---------- prose (policy pages) ---------- */

.prose { display: flex; flex-direction: column; gap: var(--sp-4); }
.prose h2 {
  font-size: var(--step-2);
  color: var(--navy);
  margin-top: var(--sp-4);
}
.prose h2:first-of-type { margin-top: 0; }
.prose p { color: var(--ink-2); }

/* ---------- process steps ----------
   These rules went missing when this file was rewritten for the slider, which
   left the About and program-detail step lists rendering as a bare <ol>. */

.steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: repeat(auto-fill, minmax(min(310px, 100%), 1fr));
  counter-reset: step;
}

.step {
  position: relative;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--sp-4);
  align-items: start;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--sp-5);
  overflow: hidden;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease),
              transform .2s var(--ease);
}
/* a thin brand rail that fills in on hover */
.step::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--green);
  transform: scaleY(0);
  transform-origin: top;
  transition: transform .25s var(--ease);
}
.step:hover {
  border-color: var(--line-strong);
  box-shadow: var(--shadow);
  transform: translateY(-2px);
}
.step:hover::before { transform: scaleY(1); }

.step-num {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--green-soft);
  color: var(--green-deep);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  flex: none;
}

.step h4 {
  font-family: var(--font-body);
  font-size: var(--step-1);
  font-weight: 700;
  color: var(--navy);
  line-height: 1.3;
}
.step p { color: var(--ink-2); font-size: var(--step--1); }

/* ---------- CTA band ----------
   Also lost in the rewrite, which left white text on a white background. */

.cta-band {
  background: linear-gradient(120deg, var(--green-deep) 0%, var(--green) 55%, #8CBF44 100%);
  padding-block: var(--sp-6);
  color: #fff;
}

.cta-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-6);
  flex-wrap: wrap;
}

.cta-copy { display: flex; flex-direction: column; gap: var(--sp-2); max-width: 46ch; }
.cta-copy h2 {
  color: #fff;
  font-size: clamp(1.4rem, 2.4vw, 2rem);
  line-height: 1.15;
}
.cta-copy p { color: #E4F1D4; font-size: var(--step-0); }

.cta-actions { display: flex; gap: var(--sp-3); flex-wrap: wrap; flex: none; }

@media (max-width: 760px) {
  .cta-inner { flex-direction: column; align-items: flex-start; gap: var(--sp-5); }
  .cta-actions { width: 100%; }
  .cta-actions .btn { flex: 1 1 auto; }
}

.cta-band .cta-outline {
  color: #fff;
  border-color: rgba(255, 255, 255, .6);
  background: transparent;
}
.cta-band .cta-outline:hover {
  color: var(--green-deep);
  background: #fff;
  border-color: #fff;
}

/* ---------- product tile fallback ----------
   56 of the 113 live products have no image; those tiles show the category
   name rather than an empty box. */

.placeholder-media {
  display: grid;
  place-items: center;
  background: linear-gradient(135deg, var(--green-soft), var(--navy-soft));
}
.placeholder-media span {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  color: var(--navy);
  opacity: .45;
}

/* ---------- listing chrome ---------- */

.result-count {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
}

.empty-note {
  color: var(--muted);
  padding: var(--sp-7) 0;
  text-align: center;
}

.card-summary {
  color: var(--ink-2);
  font-size: var(--step--1);
  line-height: 1.6;
}

/* ---------- blog / recipes switch ---------- */

.type-switch {
  display: inline-flex;
  gap: 0.25rem;
  padding: 0.3rem;
  background: var(--navy-soft);
  border-radius: var(--radius-pill);
}

.type-tab {
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--navy);
  padding: 0.55rem 1.5rem;
  border-radius: var(--radius-pill);
  transition: background-color .16s var(--ease), color .16s var(--ease);
}
.type-tab:hover {
  text-decoration: none;
  background: rgba(255, 255, 255, .7);
}
.type-tab.is-active {
  background: var(--navy);
  color: #fff;
  cursor: default;
}

/* ---------- category filter ---------- */

.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
}

.filter-chip {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--ink-2);
  background: var(--surface);
  border: 1.5px solid var(--line-strong);
  border-radius: var(--radius-pill);
  padding: 0.5rem 1rem;
  transition: background-color .16s var(--ease), border-color .16s var(--ease),
              color .16s var(--ease);
}
.filter-chip:hover {
  text-decoration: none;
  border-color: var(--green);
  color: var(--green-deep);
}
.filter-chip.is-active {
  background: var(--green);
  border-color: var(--green);
  color: #fff;
}

/* ---------- pager ---------- */

.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0;
  flex-wrap: wrap;
}

.pager-num, .pager-btn {
  display: grid;
  place-items: center;
  min-width: 40px;
  height: 40px;
  padding: 0 var(--sp-2);
  border-radius: var(--radius-sm);
  border: 1.5px solid var(--line-strong);
  background: var(--surface);
  color: var(--navy);
  font-size: var(--step--1);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  transition: background-color .16s var(--ease), border-color .16s var(--ease),
              color .16s var(--ease);
}
.pager-num:hover, .pager-btn:hover {
  text-decoration: none;
  border-color: var(--green);
  color: var(--green-deep);
}
.pager-num.is-current {
  background: var(--green);
  border-color: var(--green);
  color: #fff;
}
.pager-btn.is-off { opacity: .35; }
.pager-gap { color: var(--muted); padding-inline: var(--sp-1); }

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

.article-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: clamp(var(--sp-5), 4vw, var(--sp-7));
  align-items: start;
}
@media (max-width: 900px) { .article-layout { grid-template-columns: 1fr; } }

.article-title { font-size: clamp(1.6rem, 2.9vw, 2.375rem); }

.article-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  align-items: center;
  font-size: var(--step--1);
  color: var(--muted);
  font-weight: 600;
}

.article-hero {
  width: 100%;
  max-height: 460px;
  object-fit: cover;
  border-radius: var(--radius-lg);
}

/* The article body is admin-authored HTML from a rich-text editor, so these
   rules have to cover whatever tags it happens to contain - including inline
   font-size and colour attributes, which are overridden so the page keeps one
   readable type size, and a long unbroken URL or a table sized for a desktop,
   neither of which may be allowed to widen the page on a phone. */
.article-body { color: var(--ink-2); overflow-wrap: break-word; }
.article-body * { font-size: inherit !important; font-family: inherit !important; }
.article-body p,
.article-body div,
.article-body ul,
.article-body ol { margin: 0 0 var(--sp-4); }
.article-body h1, .article-body h2, .article-body h3, .article-body h4 {
  font-family: var(--font-display);
  color: var(--navy);
  margin: var(--sp-6) 0 var(--sp-3);
}
.article-body h2 { font-size: var(--step-2) !important; }
.article-body h3, .article-body h4 { font-size: var(--step-1) !important; }
.article-body ul, .article-body ol { padding-left: var(--sp-6); }
.article-body li { margin-bottom: var(--sp-2); }
.article-body img {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius);
  margin-block: var(--sp-4);
}
.article-body a { color: var(--green-deep); text-decoration: underline; }
.article-body strong, .article-body b { color: var(--navy); }
/* display:block is what gives the table a scroller of its own, so a wide one
   is swiped rather than pushing the whole article sideways. */
.article-body table {
  display: block;
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
  margin-bottom: var(--sp-4);
}
.article-body iframe { max-width: 100%; }
.article-body th, .article-body td {
  border: 1px solid var(--line);
  padding: var(--sp-2) var(--sp-3);
  text-align: left;
}

/* ---------- recipe sheet ----------
   The recipe body is rebuilt from the editor HTML by kgs_recipe_sections(),
   so unlike .article-body these rules style a known structure rather than
   whatever tags happened to be pasted in. */

.recipe-sheet {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}

.recipe-sheet strong { color: var(--navy); font-weight: 600; }
.recipe-sheet a { color: var(--green-deep); text-decoration: underline; }

.rs-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  font-family: var(--font-display);
  font-size: var(--step-2);
  line-height: 1.25;
  color: var(--navy);
  margin: 0 0 var(--sp-5);
}
.rs-head::before {
  content: "";
  flex: none;
  width: 26px;
  height: 3px;
  border-radius: 2px;
  background: var(--green);
}

/* A heading the dietitian typed inside a section - "Tempering", "For Dressing".
   Set below .rs-head so the two levels stay distinct. */
.rs-sub {
  font-family: var(--font-display);
  font-size: var(--step-0);
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--green-deep);
  margin: var(--sp-5) 0 var(--sp-3);
}
.rs-sub:first-child { margin-top: 0; }
.rs-ingredients .rs-sub { margin-top: var(--sp-5); }

.rs-para {
  color: var(--ink-2);
  font-size: var(--step-0);
  line-height: 1.75;
  margin: 0 0 var(--sp-3);
}
.rs-para:last-child { margin-bottom: 0; }

.rs-list {
  padding-left: var(--sp-6);
  color: var(--ink-2);
  line-height: 1.7;
}
.rs-list li { margin-bottom: var(--sp-2); }

.rs-image {
  max-width: 100%;
  height: auto;
  border-radius: var(--radius);
}

/* Ingredients - a shopping list, so it is ticked off rather than numbered. */
.rs-ingredients {
  background: var(--green-soft);
  border-radius: var(--radius-lg);
  padding: var(--sp-6);
}

.ing-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 var(--sp-7);
}
@media (max-width: 640px) { .ing-list { grid-template-columns: 1fr; } }

.ing-list li {
  position: relative;
  padding: var(--sp-3) 0 var(--sp-3) var(--sp-5);
  color: var(--ink);
  font-size: var(--step-0);
  line-height: 1.55;
  border-bottom: 1px solid rgba(90, 135, 38, .16);
}
.ing-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 1.35em;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 0 3px rgba(113, 169, 48, .18);
}

/* The last row carries no rule - a border under the final item reads as a
   missing row. Only applies while the list is in two columns. */
.ing-list li:last-child { border-bottom: none; }
@media (min-width: 641px) {
  .ing-list li:nth-last-child(2):nth-child(odd) { border-bottom: none; }
}

/* Method - genuinely a sequence, so the numbers carry information. */
.step-list {
  list-style: none;
  padding: 0;
  margin: 0;
  counter-reset: step;
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
}

.step-list li {
  counter-increment: step;
  position: relative;
  min-height: 2.25rem;
  padding-left: 3.5rem;
  color: var(--ink-2);
  font-size: var(--step-0);
  line-height: 1.7;
}
.step-list li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: -0.15rem;
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  background: var(--surface);
  border: 1.5px solid var(--green);
  color: var(--green-deep);
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 700;
}
/* a rail so the steps read as one run rather than five separate notes */
.step-list li::after {
  content: "";
  position: absolute;
  left: calc(1.125rem - 0.75px);
  top: 2.35rem;
  bottom: calc(-1 * var(--sp-5));
  width: 1.5px;
  background: linear-gradient(var(--line-strong), rgba(203, 211, 196, 0));
}
.step-list li:last-child::after { display: none; }

/* Notes, tips, nutritional value - whatever else the recipe carries. */
.rs-notes {
  background: var(--navy-soft);
  border-radius: var(--radius-lg);
  padding: var(--sp-5) var(--sp-6);
}
.rs-notes .rs-head { font-size: var(--step-1); margin-bottom: var(--sp-3); }

/* Sitting under the article now, so it is separated from the last paragraph
   and set quieter than the body it follows. */
.article-meta {
  padding-top: var(--sp-5);
  border-top: 1px solid var(--line);
}

.article-foot {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
}

.article-aside {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  position: sticky;
  top: 110px;
}
@media (max-width: 900px) { .article-aside { position: static; } }

.aside-posts { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--sp-4); }
.aside-posts a { display: flex; gap: var(--sp-3); align-items: flex-start; }
.aside-posts a:hover { text-decoration: none; }
.aside-posts img {
  width: 62px; height: 62px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  flex: none;
}
.aside-posts span { display: flex; flex-direction: column; gap: 2px; }
.aside-posts .ap-title {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--navy);
  line-height: 1.35;
}
.aside-posts time { font-size: 0.75rem; color: var(--muted); font-weight: 600; }
.aside-posts a:hover .ap-title { color: var(--green-deep); }

/* ---------- product listing and detail ---------- */

.chip-count {
  font-variant-numeric: tabular-nums;
  opacity: .65;
  margin-left: .15em;
}

.product-card .card-body { gap: var(--sp-3); }
.product-card .p-foot {
  display: flex;
  align-items: baseline;
  margin-top: auto;
  padding-top: var(--sp-1);
}
/* Sold-out rows stay legible but read as unavailable. */
.product-card.is-out .card-media img { opacity: .55; filter: saturate(.65); }
.product-card.is-out .p-price {
  color: var(--muted);
  background: var(--surface-2);
}

.media-tag.tag-out {
  background: var(--ink);
  color: #fff;
}

.product-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--sp-8);
  align-items: start;
}
@media (max-width: 900px) {
  .product-layout { grid-template-columns: 1fr; gap: var(--sp-6); }
}

.product-figure {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.product-figure img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  display: block;
}

.product-title { font-size: clamp(1.5rem, 2.7vw, 2.125rem); color: var(--navy); }

.product-price-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-3);
}
.product-price {
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: 700;
  color: var(--navy);
}
.product-weight { color: var(--muted); font-size: var(--step-0); }

.stock-pill {
  font-size: var(--step--1);
  font-weight: 600;
  padding: 0.25rem 0.7rem;
  border-radius: var(--radius-pill);
  align-self: center;
}
.stock-pill.in  { background: var(--green-soft); color: var(--green-deep); }
.stock-pill.out { background: var(--danger-soft); color: var(--danger); }

.product-desc {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  color: var(--ink-2);
  line-height: 1.75;
}
.product-desc strong { color: var(--navy); font-weight: 600; }

/* The page ends in a question, not a checkout - there is no order table and
   the clinic takes product orders by phone. */
.enquiry-box {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--sp-5);
}
.enquiry-box h2 { font-size: var(--step-1); color: var(--navy); }

.enquiry-form {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
}
.enquiry-form .input { flex: 1 1 14rem; background: var(--surface); }

.enquiry-alt {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  padding-top: var(--sp-2);
}
.enquiry-alt .btn { display: inline-flex; align-items: center; gap: var(--sp-2); }

/* Stand-in tile for the products with no photograph on file. The brand mark
   sits behind the category name so the grid keeps its rhythm. */
.product-ph {
  position: relative;
  width: 100%;
  height: 100%;
  gap: 0;
  padding: var(--sp-5);
  text-align: center;
}
.product-ph img {
  width: 44%;
  max-width: 96px;
  height: auto;
  opacity: .30;
}
.product-ph > span {
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--navy);
  opacity: .55;
}

/* ---------- page hero polish ----------
   The inner-page header was a flat grey band. It now carries a soft wash of
   the brand green and the mark set very faintly behind the type, so a page
   opens with something of the clinic's own colour rather than a plain box. */

.page-hero {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(120% 140% at 92% 0%, rgba(113, 169, 48, .13), rgba(113, 169, 48, 0) 58%),
    radial-gradient(90% 120% at 0% 100%, rgba(26, 66, 120, .07), rgba(26, 66, 120, 0) 60%),
    var(--surface-2);
}
.page-hero::after {
  content: "";
  position: absolute;
  top: 50%;
  right: -2.5rem;
  width: 260px;
  height: 300px;
  transform: translateY(-50%);
  background: url("../img/mark.png") no-repeat center / contain;
  opacity: .07;
  pointer-events: none;
}
@media (max-width: 900px) { .page-hero::after { display: none; } }

.page-hero .container { position: relative; z-index: 1; }

/* ---------- elevation, applied ----------
   Every panel on the site rests on the same first step of the elevation scale
   and lifts to the same second step. Written once here rather than repeated in
   each block, so the depth of the page stays consistent as blocks are added. */

.value-card,
.clinic-card,
.aside-card,
.step,
.testimonial,
.contact-row,
.product-figure,
.enquiry-box,
.filter-chip,
.type-switch {
  box-shadow: var(--shadow-xs);
}

.contact-form-card,
.about-figure { box-shadow: var(--shadow-lg); }

.value-card:hover,
.step:hover,
.testimonial:hover,
.contact-row:hover { box-shadow: var(--shadow); }

/* The corner radius follows the same idea: panels the size of a card share the
   card's radius, so nothing looks borrowed from another design. */
.value-card,
.clinic-card,
.aside-card,
.step,
.testimonial,
.contact-row { border-radius: var(--radius-lg); }

/* Media inside a card gets an inner line, so a photograph with a pale edge
   does not bleed into the card behind it. */
.card-media { position: relative; }
.card-media::after {
  content: "";
  position: absolute;
  inset: 0;
  box-shadow: inset 0 -1px 0 rgba(26, 66, 120, .06);
  pointer-events: none;
}

/* ---------- card detail ---------- */

/* Cards in a grid share a height, and the "view" line sits on the floor of the
   card so a row of them lines up however long the titles run. */
.grid > .card { height: 100%; display: flex; flex-direction: column; }
.grid > .card .card-body { flex: 1; }
.program-card .link-more,
.recipe-card .link-more,
.blog-card .link-more { margin-top: auto; padding-top: var(--sp-1); }

/* On hover the photograph darkens very slightly from the foot, which reads as
   the card coming forward rather than the image simply scaling. */
.card-hover .card-media::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(17, 44, 80, 0) 55%, rgba(17, 44, 80, .20) 100%);
  opacity: 0;
  transition: opacity .25s var(--ease);
  pointer-events: none;
}
.card-hover:hover .card-media::before { opacity: 1; }
.media-tag { z-index: 2; }

/* ---------- listing chrome, detailed ---------- */

.filter-chip {
  transition: background-color .16s var(--ease), border-color .16s var(--ease),
              color .16s var(--ease), box-shadow .16s var(--ease),
              transform .16s var(--ease);
}
.filter-chip:hover { box-shadow: var(--shadow-sm); transform: translateY(-1px); }
.filter-chip.is-active {
  box-shadow: 0 1px 2px rgba(26, 66, 120, .10), 0 8px 16px -10px rgba(90, 135, 38, .70);
}

.type-tab.is-active {
  box-shadow: 0 1px 2px rgba(26, 66, 120, .14), 0 8px 16px -10px rgba(26, 66, 120, .70);
}

.pager-num.is-current {
  background: var(--green);
  border-color: var(--green);
  color: #fff;
  box-shadow: 0 6px 14px -8px rgba(90, 135, 38, .8);
}
.pager-num,
.pager-btn { transition: background-color .16s var(--ease), border-color .16s var(--ease),
                         color .16s var(--ease), box-shadow .16s var(--ease); }
.pager-num:hover,
.pager-btn:not(.is-off):hover { box-shadow: var(--shadow-sm); }

/* An empty result is a state worth designing, not a stray sentence. */
.empty-note {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  padding: var(--sp-8) var(--sp-5);
  background: var(--surface);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-lg);
  text-align: center;
}
.empty-note::before {
  content: "";
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: var(--green-soft) url("../img/mark.png") no-repeat center / 60%;
  opacity: .8;
}

/* ---------- forms ---------- */

.input:focus, .select:focus, .textarea:focus {
  box-shadow: var(--ring-green);
}
.input:hover, .select:hover, .textarea:hover { border-color: var(--muted); }
.input:focus:hover, .select:focus:hover, .textarea:focus:hover { border-color: var(--green); }

/* Required fields say so quietly, rather than the visitor finding out on
   submit. */
.field label { display: flex; align-items: baseline; gap: .4em; }

/* ---------- slider motion ----------
   The slide itself cross-fades; only the button is animated, arriving a beat
   after the picture it sits on. Keyed off .is-active, which the existing
   slider script already toggles - no new JavaScript.

   The headline and body used to be animated here too. They are gone: a slide
   is now a picture with its wording baked in. */

.js .slide.is-active .slide-cta { animation: kgs-rise .6s .18s var(--ease) both; }

@media (prefers-reduced-motion: reduce) {
  .js .slide.is-active .slide-cta { animation: none; }
}

/* ---------- how it works ---------- */

.howto-layout {
  display: grid;
  grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);
  gap: clamp(var(--sp-6), 5vw, var(--sp-8));
  align-items: start;
}
@media (max-width: 900px) {
  .howto-layout { grid-template-columns: 1fr; }
}

.howto-aside {
  position: sticky;
  top: calc(92px + var(--sp-5));
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
}
@media (max-width: 900px) { .howto-aside { position: static; } }

.howto-figure {
  position: relative;
  margin: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  aspect-ratio: 4 / 5;
  box-shadow: var(--shadow-lg);
}
.howto-figure img { width: 100%; height: 100%; object-fit: cover; object-position: center 22%; }
.howto-figure figcaption {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--sp-6) var(--sp-5) var(--sp-4);
  background: linear-gradient(180deg, rgba(17,44,80,0) 0%, rgba(17,44,80,.82) 62%, rgba(17,44,80,.92) 100%);
  color: #fff;
}
.howto-figure figcaption strong {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  line-height: 1.15;
}
.howto-figure figcaption span {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: #9FD46A;
}

.howto-quote {
  color: var(--ink-2);
  font-size: var(--step-0);
  line-height: 1.7;
}

/* The timeline is no longer a floating column, so it fills its own track. */
.howto-layout .timeline { max-width: none; margin: 0; }

/* ---------- policy pages ----------
   A legal page is still a document, so it is set on a page rather than left
   floating on the site's ground, and each clause is marked so the eye can find
   the one it came for. */

.prose {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  padding: clamp(var(--sp-5), 4vw, var(--sp-7));
  gap: var(--sp-5);
}
.prose h2 {
  position: relative;
  padding-left: var(--sp-5);
  margin-top: var(--sp-5);
  font-size: var(--step-1);
}
.prose h2::before {
  content: "";
  position: absolute;
  left: 0;
  top: .3em;
  bottom: .3em;
  width: 4px;
  border-radius: 2px;
  background: var(--green);
}
.prose h2 + p { padding-left: var(--sp-5); }
.prose p { line-height: 1.75; }
.prose .alert { margin-bottom: var(--sp-2); }


/* ---------- program packages ---------- */

/* A program carries anywhere from one package to fourteen, so the row count is
   left to the grid rather than fixed at three. */
.plan-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
  gap: var(--sp-5);
  align-items: stretch;
}

.plan-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-6) var(--sp-5) var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  transition: box-shadow .2s var(--ease), transform .2s var(--ease);
}
.plan-card:hover { box-shadow: var(--shadow); transform: translateY(-3px); }

/* The longest plan is the one the clinic recommends, so it is the one thing on
   the row carrying the brand colour. */
.plan-card.is-pick {
  border-color: var(--green);
  box-shadow: var(--shadow), var(--ring-green);
}
.plan-flag {
  position: absolute;
  top: 0;
  left: var(--sp-5);
  transform: translateY(-50%);
  background: var(--green);
  color: #fff;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  padding: 0.28rem 0.6rem;
  border-radius: var(--radius-pill);
}

.plan-name {
  font-size: var(--step-1);
  line-height: 1.25;
  color: var(--navy);
}
.plan-note {
  font-size: var(--step--1);
  color: var(--muted);
  margin: 0;
}

.plan-price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0;
  margin: 0;
  padding-block: var(--sp-3);
  border-block: 1px solid var(--line);
}
.plan-amount {
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: 700;
  line-height: 1;
  color: var(--green-deep);
  font-variant-numeric: tabular-nums;
}
.plan-days {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--muted);
  letter-spacing: .04em;
  text-transform: uppercase;
}

.plan-features {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
  font-size: var(--step--1);
  color: var(--ink-2);
}
.plan-features li {
  position: relative;
  padding-left: 1.35rem;
  line-height: 1.5;
}
.plan-features li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .45em;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--green-soft);
  box-shadow: inset 0 0 0 2px var(--green);
}

/* Pinned to the floor of the card so the buttons line up across a row whatever
   the feature lists do above them. */
/* Pushed to the foot of the card so every CTA in the row lines up, whatever
   length the feature lists above them are. */
.plan-cta { margin-top: auto; }

/* A package card is 270px at its narrowest, which leaves 222px inside the
   card's padding. At the button's default 1rem text and 1.5rem sides,
   "Enquire on WhatsApp" plus its mark came to more than that and broke onto a
   second line, leaving the label sitting off-centre under the icon.

   Smaller text and tighter sides bring it to about 195px - roughly 25px of
   slack, which is what absorbs the difference between this sum and whatever
   the reader's font actually measures. nowrap then guarantees one line.
   Anything longer than this label will need the sum doing again. */
.plan-cta {
  padding-inline: var(--sp-3);
  font-size: var(--step--1);
  white-space: nowrap;
  gap: var(--sp-2);
}
.plan-cta svg { flex: 0 0 auto; }

.plan-cta[disabled] { opacity: .6; cursor: progress; }

/* The quieter second action under it. It is a plain link, so it may wrap - but
   centred, and with room above it. */
.plan-card .plan-wa { text-align: center; }

/* Whatever the checkout has to say - it is one line for the whole grid rather
   than one per card, because only one purchase can be in flight at a time. */
.buy-msg {
  margin: 0;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius);
  background: var(--surface-2);
  border-left: 3px solid var(--green);
  font-size: var(--step--1);
  color: var(--ink);
}

.buy-msg.is-bad {
  background: var(--danger-soft);
  border-left-color: var(--danger);
  color: var(--danger);
}

/* ---------- purchase receipt ---------- */

.receipt {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--sp-5);
  background: var(--surface);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

.receipt-name {
  margin: 0;
  font-size: var(--step-2);
}

.receipt-rows {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
}

.receipt-rows > div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--sp-4);
  padding-bottom: var(--sp-2);
  border-bottom: 1px dashed var(--line);
}

.receipt-rows > div:last-child { border-bottom: 0; padding-bottom: 0; }

.receipt-rows dt {
  margin: 0;
  color: var(--muted);
  font-size: var(--step--1);
}

.receipt-rows dd {
  margin: 0;
  font-weight: 600;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* A gateway reference is a long unbroken string; let it wrap rather than push
   the card sideways on a phone. */
.receipt-ref {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 500;
  word-break: break-all;
}

/* ---------- /plans - the titles, grouped ---------- */

/* One panel per group, and inside it rows on hairlines rather than a grid of
   boxes. The grid left a ragged last row under every heading - five plans in
   three columns - and each Enquire button sat at whatever x its plan name
   happened to end at. Rows give the buttons one column to line up in, and a
   panel keeps a group visibly one thing. */

/* Panels flow down one column and on to the next, so a short group does not
   leave a hole beside a tall one the way a two-column grid would. */
.plan-groups { margin-bottom: var(--sp-6); }

@media (min-width: 900px) {
  .plan-groups { columns: 2; column-gap: var(--sp-5); }
}

.plan-group {
  break-inside: avoid;
  margin-bottom: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  overflow: hidden;
}

.plan-group-head {
  padding: var(--sp-5);
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
}

.plan-group-title { display: flex; align-items: center; gap: var(--sp-3); }
.plan-group-title h2 { margin: 0; font-size: var(--step-2); }

/* How many plans are in the group, so the heading answers "how much is under
   here" before the reader counts the rows. */
.plan-group-count {
  flex: 0 0 auto;
  min-width: 26px;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--green-soft);
  color: var(--green-deep);
  font-size: var(--step--1);
  font-weight: 700;
  line-height: 1.6;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.plan-group-note {
  margin-top: var(--sp-2);
  font-size: var(--step--1);
  color: var(--muted);
  line-height: 1.6;
}

.plan-group-link { margin-top: var(--sp-3); }

.plan-list { list-style: none; margin: 0; padding: 0; }

.plan-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-5);
  transition: background-color .2s var(--ease);
}
.plan-item + .plan-item { border-top: 1px solid var(--line); }
.plan-item:hover { background: var(--green-soft); }

.plan-item-main { display: flex; flex-direction: column; gap: 1px; min-width: 0; }

.plan-item-name {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  color: var(--navy);
  line-height: 1.3;
  letter-spacing: -.01em;
}

/* A row that belongs to its own program links from the plan name itself. It
   used to carry a separate "See the full program" line, which printed the same
   sentence under all four rows of a panel. */
.plan-item-link { text-decoration: none; }
.plan-item-link:hover { color: var(--green-deep); text-decoration: none; }
.plan-item-link:hover .plan-item-arrow { transform: translateX(3px); }

.plan-item-arrow {
  display: inline-block;
  margin-left: var(--sp-2);
  color: var(--green);
  font-weight: 400;
  transition: transform .2s var(--ease);
}

/* The weight range under a title - "6 - 8 kgs". Not a price, and it must not
   be mistaken for one, so it is set as quiet supporting text. */
.plan-item-detail { font-size: var(--step--1); color: var(--muted); line-height: 1.5; }

.plan-item-cta { flex: 0 0 auto; }

.plan-foot {
  max-width: 46rem;
  margin-inline: auto;
  text-align: center;
  font-size: var(--step--1);
  color: var(--muted);
  line-height: 1.7;
}

.cta-actions .btn .icon { vertical-align: -3px; margin-right: 6px; }

@media (max-width: 560px) {
  .plan-group-head { padding: var(--sp-4); }
  .plan-item { padding: var(--sp-3) var(--sp-4); }
  .plan-item-name { font-size: var(--step-0); }
}

/* Below a phone-and-a-half the button no longer fits beside a two-line plan
   name, so the row stacks and the button goes full width under it. */
@media (max-width: 380px) {
  .plan-item { flex-direction: column; align-items: stretch; gap: var(--sp-3); }
  .plan-item-cta { justify-content: center; }
}


/* ---------- customer account ---------- */

/* The account pages are read, not browsed: a fixed rail of destinations on the
   left, the record itself on the right. Same shape as the program detail page,
   so the site does not gain a second idea of what an aside is. */
.account-layout {
  display: grid;
  grid-template-columns: 260px minmax(0, 1fr);
  gap: clamp(var(--sp-5), 4vw, var(--sp-7));
  align-items: start;
}
@media (max-width: 900px) {
  .account-layout { grid-template-columns: 1fr; }
}

.account-nav {
  position: sticky;
  top: 96px;
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  padding: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
}

.account-who {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--line);
}
.account-initial {
  flex: 0 0 42px;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 700;
  color: var(--green-deep);
  background: var(--green-soft);
}
.account-who-text { display: flex; flex-direction: column; min-width: 0; }
.account-who-text strong {
  color: var(--navy);
  font-size: var(--step-0);
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.account-who-text span { font-size: var(--step--1); color: var(--muted); }

.account-links { display: flex; flex-direction: column; gap: 2px; }
.account-links a {
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: var(--step--1);
  color: var(--ink-2);
  text-decoration: none;
  transition: background-color .15s var(--ease), color .15s var(--ease);
}
.account-links a:hover { background: var(--surface-2); color: var(--navy); }
.account-links a[aria-current="page"] {
  background: var(--green-soft);
  color: var(--green-deep);
}
/* Sign out is the one destructive thing on the rail, so it sits apart. */
.account-out {
  margin-top: var(--sp-3);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--line);
  border-radius: 0;
  color: var(--muted);
}
.account-links .account-out:hover { color: var(--danger); background: transparent; }

/* On a phone the rail took the whole first screen, so every account page
   opened on its own menu and the record itself started below the fold. Below
   900px it lies down into a strip of tabs that scrolls sideways instead.
   This block has to come after the rules above, not before them - it overrides
   them at equal specificity, so the cascade decides it on order. */
@media (max-width: 900px) {
  .account-nav {
    position: static;
    padding: var(--sp-4);
    gap: var(--sp-3);
  }
  .account-who { padding-bottom: var(--sp-3); }

  .account-links {
    flex-direction: row;
    gap: var(--sp-2);
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .account-links::-webkit-scrollbar { display: none; }

  .account-links a {
    flex: 0 0 auto;
    white-space: nowrap;
    border-radius: var(--radius-pill);
    background: var(--surface-2);
  }
  .account-links a[aria-current="page"] { background: var(--green-soft); }

  .account-links .account-out {
    margin-top: 0;
    padding-top: var(--sp-3);
    border-top: 0;
    border-radius: var(--radius-pill);
  }
}

.account-body { min-width: 0; }

/* The one thing a client opens this section to check. */
.plan-status {
  padding: var(--sp-6);
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  align-items: flex-start;
}
.plan-status.is-on {
  border-color: var(--green);
  box-shadow: var(--shadow), var(--ring-green);
}
.plan-status h2 { margin: 0; }
.plan-status-line { margin: 0; color: var(--ink-2); font-size: var(--step--1); }

.account-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-5);
  margin: 0;
  padding: var(--sp-5);
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
}
.account-stats div { display: flex; flex-direction: column; gap: 2px; }
.account-stats dt {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 700;
  color: var(--green-deep);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.account-stats dd {
  margin: 0;
  font-size: var(--step--1);
  color: var(--muted);
  letter-spacing: .06em;
  text-transform: uppercase;
  font-weight: 600;
}


/* ---------- record tables ---------- */

/* A bill has five columns and a phone has none to spare, so the table scrolls
   inside its own box rather than pushing the page sideways. */
.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--step--1);
  min-width: 480px;
}
.data-table th,
.data-table td {
  padding: var(--sp-3) var(--sp-4);
  text-align: left;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.data-table thead th {
  background: var(--surface-2);
  color: var(--muted);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  font-size: 0.6875rem;
}
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table tbody tr:hover { background: var(--surface-2); }
.data-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.data-table .muted-cell { color: var(--muted); }

.data-table tfoot td {
  background: var(--surface-2);
  border-bottom: 0;
  font-weight: 600;
}
.data-table tfoot .is-total td {
  font-size: var(--step-0);
  font-weight: 700;
  color: var(--navy);
}


/* ---------- plan history ---------- */

.plan-history {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.plan-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
}
.plan-row.is-on { border-color: var(--green); box-shadow: var(--shadow-xs), var(--ring-green); }
.plan-row-main { min-width: 0; }
.plan-row-main h3 { margin: 0; font-size: var(--step-0); color: var(--navy); }
.plan-row-dates { margin: 2px 0 0; font-size: var(--step--1); color: var(--muted); }
.plan-row-state { display: flex; align-items: center; gap: var(--sp-3); }
.pill-on { background: var(--green); color: #fff; }
.plan-left { font-size: var(--step--1); font-weight: 600; color: var(--green-deep); }


/* ---------- sign in and register ---------- */

.auth-wrap {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  gap: clamp(var(--sp-5), 4vw, var(--sp-7));
  align-items: start;
  max-width: 62rem;
}
@media (max-width: 820px) {
  .auth-wrap { grid-template-columns: 1fr; }
}

.auth-card {
  padding: clamp(var(--sp-5), 4vw, var(--sp-7));
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}

.auth-aside {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  align-items: flex-start;
  padding: var(--sp-6) var(--sp-5);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
  border: 1px solid var(--line);
}
.auth-aside h2 { margin: 0; font-size: var(--step-1); }
.auth-aside p { margin: 0; color: var(--ink-2); font-size: var(--step--1); }
.auth-aside .btn { width: 100%; }

/* ---------- profile form ---------- */

.profile-form {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  overflow: hidden;
}

/* Each block is a titled section with a rule under it, so "your details" and
   "sign-in" read as two decisions rather than one long list of boxes. */
.pf-block {
  padding: clamp(var(--sp-5), 3.5vw, var(--sp-6));
  border-bottom: 1px solid var(--line);
}

.pf-head { margin-bottom: var(--sp-5); }
.pf-head h2 {
  margin: 0;
  font-size: var(--step-1);
  color: var(--navy);
}
.pf-head p {
  margin: var(--sp-2) 0 0;
  font-size: var(--step--1);
  color: var(--muted);
}

/* Two even columns. The fields used to be flex children on a start-aligned
   parent, so each one shrank to whatever its value happened to be - four
   different widths down one form. */
.pf-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-5);
}
.pf-grid .pf-wide { grid-column: 1 / -1; }
.pf-grid .field { min-width: 0; }
.pf-grid .input,
.pf-grid .textarea { width: 100%; }
.pf-grid .textarea { resize: vertical; min-height: 5.5rem; }

@media (max-width: 620px) {
  .pf-grid { grid-template-columns: 1fr; }
}

/* A field that is shown but not editable. The lock is the explanation - a
   greyed box on its own just looks broken. */
.input-locked { position: relative; display: block; }
.input-locked .input {
  background: var(--surface-2);
  color: var(--muted);
  padding-right: 2.4rem;
  cursor: not-allowed;
}
.input-locked svg {
  position: absolute;
  right: var(--sp-4);
  top: 50%;
  transform: translateY(-50%);
  color: var(--muted);
  pointer-events: none;
}

.pf-warn {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  margin: var(--sp-5) 0 0;
  padding: var(--sp-4);
  border-radius: var(--radius);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: var(--step--1);
  line-height: 1.5;
}
.pf-warn svg { flex: 0 0 auto; margin-top: .15em; color: var(--navy); }

/* The actions sit on their own bar at the foot of the card, so a long form
   still ends somewhere obvious. */
.pf-foot {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  padding: var(--sp-5) clamp(var(--sp-5), 3.5vw, var(--sp-6));
  background: var(--surface-2);
  border-bottom: 0;
}

.pf-aside {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-4);
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-left: 3px solid var(--green);
  border-radius: var(--radius);
  background: var(--surface);
}
.pf-aside-icon {
  flex: 0 0 40px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--green-soft);
  color: var(--green-deep);
}
/* ---------- slider management ---------- */

.slide-card {
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
}
/* A hidden slide is dimmed rather than moved, so the running order on this
   page is the running order on the home page. */
.slide-card.is-off { opacity: .62; }

.slide-card-head {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr) auto;
  gap: var(--sp-5);
  align-items: start;
}
@media (max-width: 780px) {
  .slide-card-head { grid-template-columns: 1fr; }
}

.slide-thumb {
  aspect-ratio: 16 / 7;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface-2);
  display: grid;
  place-items: center;
}
.slide-thumb img { width: 100%; height: 100%; object-fit: cover; }
.slide-missing {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--danger);
}

.slide-meta { display: flex; flex-direction: column; gap: var(--sp-2); min-width: 0; }
.slide-meta p { margin: 0; }
.slide-pos {
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
}
.slide-alt-text { color: var(--ink-2); font-size: var(--step--1); }

.slide-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.slide-actions form { margin: 0; }
.btn.is-danger { color: var(--danger); border-color: var(--danger); }
.btn.is-danger:hover { background: var(--danger); color: #fff; }

.slide-edit { margin-top: var(--sp-5); border-top: 1px solid var(--line); padding-top: var(--sp-4); }
.slide-edit summary {
  cursor: pointer;
  font-weight: 700;
  font-size: var(--step--1);
  color: var(--green-deep);
}
.slide-edit[open] summary { margin-bottom: var(--sp-5); }

.slide-form-foot { margin-top: var(--sp-5); }

/* A checkbox and its words on one line, aligned on the box rather than the
   text baseline. */
.check {
  display: flex !important;
  align-items: center;
  gap: var(--sp-3);
  font-weight: 500;
  cursor: pointer;
}
.check input { width: 17px; height: 17px; accent-color: var(--green); }

.field input[type="file"] {
  padding: var(--sp-3);
  font-size: var(--step--1);
}


/* ---------- register form ---------- */

/* Narrower than the account pages: this one is a form and nothing else, and a
   field 900px wide reads as a mistake. */
.register-wrap {
  max-width: 54rem;
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
}

.register-form {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}

/* Age and BMI are worked out, not typed. They are shown flat and unfocusable
   so nobody waits for a cursor that is not coming. */
.input.is-derived {
  background: var(--surface-2);
  border-style: dashed;
  color: var(--navy);
  font-weight: 700;
  cursor: default;
}
.input.is-derived::placeholder { font-weight: 400; color: var(--muted); }

.label-unit {
  font-weight: 500;
  color: var(--muted);
  text-transform: none;
  letter-spacing: 0;
}

/* +91 sits inside the field rather than beside it, so the country code cannot
   be mistaken for the first two digits of the number. */
.input-prefixed { display: flex; align-items: stretch; }
.input-prefix {
  display: flex;
  align-items: center;
  padding: 0 var(--sp-4);
  border: 1px solid var(--line);
  border-right: 0;
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink-2);
  font-weight: 600;
  font-size: var(--step--1);
  white-space: nowrap;
}
.input-prefixed .input {
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  min-width: 0;
  flex: 1;
}

.input-reveal { position: relative; display: block; }
.input-reveal .input { width: 100%; padding-right: 2.8rem; }
.reveal-btn {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.reveal-btn:hover { color: var(--navy); background: var(--surface-2); }
.reveal-btn.is-on { color: var(--green-deep); }

.pf-foot-note {
  flex-basis: 100%;
  margin: 0;
  font-size: var(--step--1);
  color: var(--muted);
}

.form-note.center { text-align: center; }

.pf-aside strong { color: var(--navy); font-size: var(--step-0); }
.pf-aside p {
  margin: var(--sp-2) 0 0;
  font-size: var(--step--1);
  color: var(--ink-2);
  line-height: 1.55;
}


/* ---------- measurements ---------- */

.metric-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
  gap: var(--sp-4);
}

.metric {
  display: flex;
  flex-direction: column;
  padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-xs);
}
/* "Subcutaneous fat" wraps to two lines and "BMI" does not, which left the
   numbers on one row sitting at different heights. The label holds two lines
   whether it needs them or not, so every value starts on the same line. */
.metric-label {
  margin: 0;
  min-height: 2.4em;
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  line-height: 1.2;
  color: var(--muted);
}
.metric-value {
  margin: var(--sp-2) 0 0;
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 700;
  line-height: 1;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
}
.metric-unit {
  margin-left: .28em;
  font-family: var(--font-body);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--muted);
}
/* Pushed to the floor of the tile, so the deltas line up across the row even
   where a label above them took an extra line. */
.metric-delta {
  margin: var(--sp-3) 0 0;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
  font-size: var(--step--1);
  font-weight: 600;
  margin-top: auto;
}

/* Good and bad, not up and down. Weight falling is progress and skeletal
   muscle falling is not, so the direction alone cannot pick the colour - the
   view passes which way each reading is meant to go. */
.is-good { color: var(--green-deep); }
.is-bad  { color: var(--danger); }
.is-flat { color: var(--muted); font-weight: 500; }

.metric-since {
  margin: 0;
  padding: var(--sp-4) var(--sp-5);
  border-radius: var(--radius);
  background: var(--surface-2);
  font-size: var(--step-0);
}
.metric-since strong {
  font-family: var(--font-display);
  font-size: var(--step-1);
}

/* "vs last visit" is the caption, not the number - it stays out of the way. */
.delta-cap {
  font-weight: 500;
  color: var(--muted);
  white-space: nowrap;
}

/* The change riding beside a number in a table cell. */
.cell-delta {
  margin-left: var(--sp-2);
  font-size: 0.6875rem;
  font-weight: 700;
}

/* A hint under a field, not a second label - quieter than the label above it,
   so the two are never confused. */
.field-hint {
  display: block;
  margin-top: var(--sp-2);
  font-size: var(--step--1);
  color: var(--muted);
  line-height: 1.5;
}

.input:disabled {
  background: var(--surface-2);
  color: var(--muted);
  cursor: not-allowed;
}

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  padding: clamp(var(--sp-6), 6vw, var(--sp-8)) var(--sp-5);
  text-align: center;
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface-2);
  color: var(--muted);
}
.empty-state p { margin: 0; }


/* ===========================================================================
   Basket, checkout and orders
   ===========================================================================
   The product card used to be one big anchor. It now has to hold an Add
   button as well, and a form inside an anchor is not valid HTML, so the card
   is a <div>, the anchor covers the picture and the name through an overlay,
   and the button sits above that overlay on its own.
   ------------------------------------------------------------------------ */

.product-card { position: relative; display: flex; flex-direction: column; }
.product-card .card-link { display: block; color: inherit; }
.product-card .card-link:hover { text-decoration: none; }
.product-card .card-link::after {
  content: ""; position: absolute; inset: 0; z-index: 0;
}

.card-add {
  position: relative; z-index: 1;
  margin-top: auto;
  padding: 0 var(--sp-5) var(--sp-5);
}
/* Sold out, or unpriced: the tile is a link through to the enquiry form and
   carries no button, so nothing here needs a disabled state. */
.product-card.is-out .card-add { display: none; }

/* ---------- add to basket, on the product page ---------- */

.buy-box { display: flex; flex-direction: column; gap: var(--sp-3); }
.buy-form { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.buy-form .btn { flex: 1 1 12rem; }

/* Wide enough for two digits and the spinner, and no wider - a full-width
   number box next to a button reads as the more important of the two. */
.qty-input {
  width: 5.5rem;
  flex: none;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* ---------- the basket ---------- */

.cart-lines { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-3); }

.cart-line {
  display: grid;
  grid-template-columns: 72px 1fr auto auto auto;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
/* A line that cannot be sold is dimmed rather than hidden, so it is obvious
   why the total does not match what was picked out. */
.cart-line.is-out { opacity: .6; }

.cart-thumb {
  width: 72px; height: 72px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface-2);
  display: grid; place-items: center;
}
.cart-thumb img { width: 100%; height: 100%; object-fit: cover; }
.cart-thumb .placeholder-media img { width: 60%; height: auto; object-fit: contain; opacity: .5; }

.cart-line-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cart-name { font-weight: 700; color: var(--navy); }
.cart-name:hover { color: var(--green-deep); }
.cart-weight,
.cart-rate { font-size: var(--step--1); color: var(--muted); }

.cart-flag { font-size: var(--step--1); font-weight: 600; margin-top: 2px; }
.cart-flag.out  { color: var(--danger); }
.cart-flag.warn { color: var(--green-deep); }

.cart-line-total {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-0);
  color: var(--navy);
  font-variant-numeric: tabular-nums;
  min-width: 4.5rem;
  text-align: right;
}

.cart-actions {
  display: flex; align-items: center; gap: var(--sp-4);
  flex-wrap: wrap;
  margin-top: var(--sp-4);
}

/* Remove, on the row it belongs to.
   Sits quiet in grey until the row is hovered or the button is focused, then
   turns red - present when wanted, not shouting on every line. It is a real
   button with a title and an aria-label, so it is reachable by keyboard and
   announced properly; the icon alone would say nothing to a screen reader. */
.cart-remove {
  display: grid; place-items: center;
  width: 38px; height: 38px;
  margin-left: var(--sp-2);
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--muted);
  cursor: pointer;
  transition: color .15s var(--ease), background-color .15s var(--ease), border-color .15s var(--ease);
}
.cart-line:hover .cart-remove { color: var(--ink-2); }
.cart-remove:hover {
  color: var(--danger);
  background: var(--danger-soft);
  border-color: var(--danger-soft);
}
.cart-remove:focus-visible {
  outline: 2px solid var(--danger);
  outline-offset: 1px;
  color: var(--danger);
}

/* ---------- money summary, shared by the basket and the checkout ---------- */

.cart-summary { padding: var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-3); }

.sum-row {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--sp-4);
  font-size: var(--step-0);
  font-variant-numeric: tabular-nums;
}
.sum-row span:first-child { color: var(--ink-2); }

/* The delivery charge, which this page cannot know. Set smaller and quieter
   than the figures around it so it reads as a note rather than an amount. */
.sum-pending {
  font-size: var(--step--1);
  color: var(--muted);
  font-style: italic;
}

.sum-total {
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-1);
  color: var(--navy);
}
.sum-total span:first-child { color: var(--navy); }

.sum-lines { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-3); }
.sum-lines li {
  display: flex; justify-content: space-between; gap: var(--sp-3);
  font-size: var(--step--1);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--line);
}
.sum-lines li:last-child { border-bottom: 0; padding-bottom: 0; }
.sum-name { display: flex; flex-direction: column; gap: 1px; color: var(--navy); font-weight: 600; }
.sum-name small { font-weight: 400; color: var(--muted); }
.sum-amt { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---------- checkout ---------- */

.checkout-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: var(--sp-6);
  align-items: start;
}
.checkout-form .card { padding: var(--sp-5); }
/* The summary follows down a long address form on a wide screen; on a narrow
   one it is simply the next thing after it. */
.checkout-summary { position: sticky; top: var(--sp-6); }
.checkout-summary .card { padding: var(--sp-5); }

.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); }
.field-row-3 { grid-template-columns: 1fr 1fr 1fr; }
.opt { font-weight: 400; color: var(--muted); }

/* ---------- an order ---------- */

.status-pill {
  display: inline-block;
  padding: 2px var(--sp-3);
  border-radius: var(--radius-pill);
  font-size: 0.6875rem; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase;
  background: var(--surface-2); color: var(--ink-2);
  white-space: nowrap;
}
.st-confirmed,
.st-pending   { background: var(--green-soft); color: var(--green-deep); }
.st-packed,
.st-shipped   { background: #fff3d6; color: #8a5b00; }
.st-delivered { background: var(--green-soft); color: var(--green-deep); }
.st-cancelled { background: var(--danger-soft); color: var(--danger); }

.order-meta {
  display: flex; flex-wrap: wrap; gap: var(--sp-5);
  padding: var(--sp-4) var(--sp-5);
}
.order-meta > div { display: flex; flex-direction: column; gap: 2px; }
.meta-k {
  font-size: 0.6875rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.meta-v { font-weight: 600; color: var(--navy); }
.meta-v.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--step--1); }

.address { line-height: 1.7; }

/* The thank-you page. */
.done-head { text-align: center; }
.done-tick {
  display: grid; place-items: center;
  width: 76px; height: 76px; margin: 0 auto;
  border-radius: 50%;
  background: var(--green-soft); color: var(--green-deep);
}

@media (max-width: 900px) {
  .checkout-layout { grid-template-columns: 1fr; }
  .checkout-summary { position: static; }
}

@media (max-width: 600px) {
  /* The picture, the name and the price stay on one line; the quantity box
     drops underneath, where it has room to be tapped. */
  .cart-line {
    grid-template-columns: 56px 1fr auto auto;
    gap: var(--sp-3);
  }
  .cart-thumb { width: 56px; height: 56px; }
  .cart-line-qty { grid-column: 2 / -1; }
  .qty-input { width: 5rem; }

  .field-row,
  .field-row-3 { grid-template-columns: 1fr; }

  .order-meta { gap: var(--sp-4); }
}

/* ---------- staff orders screen ---------- */

/* Filters on the left, the search box on the right, and on a narrow screen the
   search drops underneath the filters rather than squeezing them. */
.orders-bar {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: var(--sp-4); flex-wrap: wrap;
}
.orders-bar .filter-bar { flex: 1 1 20rem; }

.orders-search { display: flex; gap: var(--sp-2); flex: 0 1 22rem; }
.orders-search .input { min-width: 0; }

.manage-cols {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
  gap: var(--sp-5);
  align-items: start;
}
.manage-cols .card { padding: var(--sp-5); }

/* The address is here to be copied onto a parcel, so it is set larger than
   body text and left selectable as one block. */
.address-block {
  font-size: var(--step-0);
  line-height: 1.8;
  padding: var(--sp-4);
  background: var(--surface-2);
  border-radius: var(--radius-sm);
  user-select: all;
}

@media (max-width: 900px) {
  .manage-cols { grid-template-columns: 1fr; }
  .orders-search { flex: 1 1 100%; }
}

/* ---------- recipe reel ----------
   A written recipe can carry a link to the reel of the same dish. The clinic
   pastes the link in the admin panel; these are the two places it shows.

   Instagram's own colours are deliberately not used. A magenta gradient button
   in the middle of a page built on green and navy reads as an advertisement
   someone else put there. The mark alone says whose it is. */

.reel-card {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  background: var(--green-soft);
  border: 1px solid var(--green);
  border-radius: var(--radius-lg);
  color: inherit;
  text-decoration: none;
  transition: transform .15s var(--ease), box-shadow .15s var(--ease), background-color .15s var(--ease);
}
.reel-card:hover {
  background: #E7F2D9;
  transform: translateY(-2px);
  box-shadow: var(--shadow-sm);
}

.reel-play {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: var(--radius-pill);
  background: var(--green);
  color: #fff;
  /* The triangle is optically off-centre in its own box; nudge it back. */
  padding-left: 3px;
}

.reel-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.reel-title {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--navy);
  line-height: 1.3;
}
.reel-sub { font-size: var(--step--1); color: var(--ink-2); }

.reel-mark { flex: 0 0 auto; color: var(--green-deep); margin-left: auto; }

/* Below the mark is the first thing to go - the play button already says this
   is a video, and the words already say where it goes. */
@media (max-width: 520px) {
  .reel-card { padding: var(--sp-4); gap: var(--sp-3); }
  .reel-mark { display: none; }
  .reel-sub { display: none; }
}

/* The mark on a recipe card in the list, opposite the category tag. */
.media-reel {
  position: absolute;
  right: var(--sp-3);
  top: var(--sp-3);
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  background: rgba(27, 33, 25, .78);
  color: #fff;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  padding: 0.3rem 0.5rem;
  border-radius: var(--radius-sm);
}

/* The clinic-hours row on /contact. It is the one row in that list with
   nothing to tap, so it drops the hover lift and the pointer that go with the
   four links above it. */
.contact-row.is-static { cursor: default; }
.contact-row.is-static:hover { border-color: var(--line); box-shadow: none; }

.contact-note {
  font-size: var(--step--1);
  color: var(--muted);
  line-height: 1.5;
}

/* Delivery address on the basket page. Sits inside the summary card, above the
   WhatsApp button, so it reads as part of placing the order rather than as a
   form standing between the customer and it. */
.ship-block {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding-top: var(--sp-4);
  margin-top: var(--sp-2);
  border-top: 1px solid var(--line);
}

.ship-head {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-0);
  color: var(--navy);
}

.ship-block textarea.input { resize: vertical; min-height: 4.2rem; }

/* City takes the room it needs for a name; the pincode is six digits and never
   more, so it is not given half the row. */
.ship-row {
  display: grid;
  grid-template-columns: 1fr 8rem;
  gap: var(--sp-2);
}

@media (max-width: 420px) {
  .ship-row { grid-template-columns: 1fr; }
}
