/* ==========================================================================
   Canoli marketing home
   ==========================================================================

   Loaded only by pages/marketing/home-canoli.html, through the extra_css
   block in layouts/marketing.html. Everything is scoped under .cnl because
   this file loads alongside marketing.css, trust.css and about.css, which
   the other marketing pages share; an unscoped rule here would restyle
   pages nobody asked me to touch.

   Colours come from tokens-canoli.css. No hex literals: the house rule is
   variables, and the palette has to follow the brand rather than be frozen
   into this bundle. Where a translucent rung is needed that the palette
   does not hold, color-mix derives it from a token rather than inventing
   a value.

   Built from the approved comp, Figma boards "06 Marketing home / Canoli"
   and "07 Marketing home / Canoli mobile".
   ========================================================================== */

.cnl {
  --cnl-gut: 64px;              /* desktop side gutter, from the comp */
  --cnl-max: 1312px;            /* content width inside the gutters */
  --cnl-hairline: color-mix(in srgb, var(--text-on-dark) 18%, transparent);
  /* The boundary of a CONTROL on a dark ground, as distinct from the
     decorative hairline above.

     WCAG 1.4.11 asks 3:1 of the visual boundary of a control, and the same
     18% that is right for a divider measures 1.70:1 on the damson ground.
     Thickening it would not help: 1.4.11 measures contrast, not width, and a
     heavier secondary button would outweigh the primary beside it.

     38% gives 3.35:1, headroom over the threshold without climbing toward
     the butter primary's 12.54:1 and competing with it. */
  --cnl-control-edge: color-mix(in srgb, var(--text-on-dark) 38%, transparent);
  --cnl-hairline-dark: color-mix(in srgb, var(--text-primary) 12%, transparent);

  font-family: var(--font-primary);
  color: var(--text-primary);
  background: var(--bg-primary);
}

.cnl *,
.cnl *::before,
.cnl *::after { box-sizing: border-box; }

.cnl h1, .cnl h2, .cnl h3 {
  font-family: var(--font-display);
  font-weight: 700;
  margin: 0;
}

.cnl p, .cnl ul, .cnl ol, .cnl dl { margin: 0; }
.cnl ul, .cnl ol { list-style: none; padding: 0; }

/* Shared eyebrow: the small tracked label above a section headline. */
.cnl .cnl-eyebrow {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--brand-gold);
}

/* Keyboard focus, for everything, once.
   ---------------------------------------------------------------------------
   Six focus rules existed, each written for the control in front of whoever
   was working that day, and between them they missed the nav links, the
   drawer links, the burger, the trust-page link and the cadence toggle.
   Those fell back to Chrome's default ring: visible, but a different shape
   and colour from the gold ring everything else uses, so tabbing the page
   looked like two designs fighting.

   Enumerating controls is what produced the gap, so this does not enumerate
   them. It is deliberately placed BEFORE the specific rules below: a
   type-plus-class selector loses to their class-plus-class, so anything
   wanting a different offset still gets it, and anything new is covered the
   moment it exists.

   marketing.css cannot do this job: its .nav a:focus-visible never matches,
   because the Canoli nav is .cnl-nav. */
.cnl a:focus-visible,
.cnl button:focus-visible,
.cnl input:focus-visible,
.cnl select:focus-visible,
.cnl textarea:focus-visible,
.cnl [tabindex]:focus-visible {
  outline: 2px solid var(--cnl-focus);
  outline-offset: 3px;
  border-radius: var(--radius-chip-sm);
}

/* THE RING'S COLOUR FOLLOWS THE GROUND IT LANDS ON.
   ---------------------------------------------------------------------------
   This rule originally hardcoded --brand-gold, which was written to close a
   COVERAGE gap: five controls had no focus rule at all. Coverage was verified
   through the CSSOM and the colour was never checked, so the fix worked
   everywhere it was already fine and was invisible everywhere it was needed
   most. Measured, against WCAG 2.4.11's 3:1:

     ground                gold    damson
     damson  #241426      12.54      1.00   gold
     ink-deep#180D1A      13.57      1.08   gold
     clay    #715575       4.63      2.71   gold
     punch   #FF5A75       2.16      5.79   damson
     butter  #FFD66B       1.00     12.54   damson
     shell   #FDFBFA       1.35     16.92   damson
     white   #FFFFFF       1.39     17.46   damson
     cream   #F5EAE0       1.18     14.74   damson

   So a keyboard user tabbed the four pricing CTAs, the cadence toggle, the
   compare summary, the newsletter field and all five demo sliders with a ring
   at 1.35:1. Punch was failing too, which the review did not catch.

   --cnl-focus defaults to gold because most of this page is dark, and the
   light and saturated sections override it. Setting the VARIABLE rather than
   repeating the rule means a control inherits the right ring from wherever it
   sits, including a white card inside a dark section, and a section added
   later gets a correct ring by declaring one token. */
.cnl { --cnl-focus: var(--brand-gold); }

.cnl .cnl-why-now,
.cnl .cnl-proof,
.cnl .cnl-substrate,
.cnl .cnl-pricing,
.cnl .cnl-news { --cnl-focus: var(--text-primary); }

/* Light surfaces that sit INSIDE a dark section, so they cannot inherit the
   correct ring from their section. The demo card is the important one: all
   five sliders live on white inside a damson band. */
.cnl .cnl-demo__card,
.cnl .cnl-tier,
.cnl .cnl-logo { --cnl-focus: var(--text-primary); }

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */

.cnl .cnl-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 15px 26px;
  border: 1px solid transparent;
  border-radius: var(--radius-button);
  font-family: var(--font-primary);
  font-size: 16px;
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.cnl .cnl-btn--primary { background: var(--brand-gold); color: var(--text-primary); }
.cnl .cnl-btn--primary:hover { background: var(--brand-gold-hover); }

/* Ghost sits on the dark hero, so its border is derived from the on-dark
   text colour rather than the light-ground border token. */
.cnl .cnl-btn--ghost {
  background: transparent;
  color: var(--text-on-dark);
  border-color: var(--cnl-control-edge);
}
.cnl .cnl-btn--ghost:hover { border-color: var(--text-on-dark-subtle); }

.cnl .cnl-btn--dark { background: var(--text-primary); color: var(--text-on-dark); }
.cnl .cnl-btn--dark:hover { background: var(--ink-deep); }

.cnl .cnl-btn--sm { padding: 10px 18px; font-size: 14px; }
.cnl .cnl-btn--lg { padding: 17px 32px; font-size: 17px; }

/* var(--cnl-focus), NOT var(--brand-gold), and the same correction was made
   to five other rules in this file at the same time.

   The base focus rule near the top of this stylesheet already resolves the
   ring per section: gold on the damson bands, damson ink on cream and punch.
   Six rules then hardcoded gold and, being more specific, won. So every
   control sitting on a light ground drew a gold ring in the gap its
   outline-offset opens: 1.39:1 on white, 1.35:1 on shell, 2.16:1 on punch,
   against the 3:1 WCAG 2.4.11 asks of a focus indicator. Those six covered
   the five demo sliders, the swap and reset buttons, every pricing CTA, the
   newsletter input and its sign-up button.

   HOW IT SURVIVED A REVIEW THAT WENT LOOKING FOR IT. I measured
   --cnl-focus on each element, found it correctly damson on the light
   sections, and reported the finding as disproven. The token was never the
   problem. The outline did not READ the token, and resolving a variable tells
   you nothing about whether the declaration that matters uses it. Measure the
   rendered outline-color, not the custom property you hope feeds it.

   Swapping to the token is safe on the dark sections too, because there it
   already resolves to the same gold these rules were hardcoding. */
.cnl .cnl-btn:focus-visible {
  outline: 2px solid var(--cnl-focus);
  outline-offset: 3px;
}

/* --------------------------------------------------------------------------
   Nav
   -------------------------------------------------------------------------- */

/* The nav returns on scroll UP, not on every scroll.
   ---------------------------------------------------------------------------
   Pinning it permanently would put a solid damson bar over the hero artwork,
   which is the one part of this page that is genuinely its own. Hiding it for
   the whole page is what left roughly 3,800px with no route forward. Coming
   back only when someone scrolls up answers both: reading down is
   undisturbed, and the moment a visitor looks for a way on, it is there.

   canoli-nav.js adds the classes. Without JavaScript the nav stays exactly
   where it was, at the top of the page, which is the pre-existing behaviour
   and perfectly usable. */
.cnl .cnl-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 20px var(--cnl-gut);
  background: var(--text-primary);
  position: relative;
  z-index: 3;
}

.cnl .cnl-nav--pinned {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 40;
  transform: translateY(-100%);
  transition: transform 0.28s ease;
  border-bottom: 1px solid var(--cnl-hairline);
}

.cnl .cnl-nav--shown { transform: translateY(0); }

/* Fixed positioning takes the nav out of flow, so the page would jump by its
   height at the moment it pins. The spacer holds that height open. */
.cnl .cnl-nav__spacer { display: none; }
.cnl .cnl-nav__spacer--on { display: block; }

.cnl .cnl-nav__logo { display: inline-flex; }
.cnl .cnl-nav__logo-img { height: 26px; width: auto; display: block; }

.cnl .cnl-nav__links {
  display: flex;
  align-items: center;
  gap: 28px;
}

.cnl .cnl-nav__links a {
  color: var(--text-on-dark-subtle);
  text-decoration: none;
  font-size: 15px;
  font-weight: 500;
}
/* :not(.cnl-btn) is load-bearing, and its absence was a real failure on the
   most important control on the page.

   These two rules used to be `a:hover` and `.cnl-btn`. The first is (0,3,1),
   the second (0,3,0), so on hover the generic link colour BEAT the button's
   own: the gold "Start free month" CTA repainted its label from damson ink to
   --text-on-dark and rendered #FAF4EF on the #E0A11A hover fill. Measured
   live: 2.07:1 at 15px weight 500, which is normal text needing 4.5. The
   drawer's copy of the same button was never affected and sits at 12.54:1,
   which is why this survived: the bug only exists in the state you cannot see
   in a screenshot, on the one element every visitor points at before they
   click it.

   Excluding the button from the link rule is the fix rather than raising the
   button rule's specificity, because a button is not a nav link that happens
   to be styled: it has its own colour system, and any future hover state
   added to this list should not reach into it either. */
.cnl .cnl-nav__links a:not(.cnl-btn):hover { color: var(--text-on-dark); }
.cnl .cnl-nav__links .cnl-btn { color: var(--text-primary); }

/* Burger: hidden on desktop. 44px box is the tap target, the bars are the
   glyph inside it. */
.cnl .cnl-burger {
  display: none;
  width: 44px;
  height: 44px;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
}
.cnl .cnl-burger__bar {
  display: block;
  width: 22px;
  height: 2px;
  /* The pill token, not a 1px literal: on a 2px bar it renders identically
     and keeps the shape on the documented scale. */
  border-radius: var(--radius-button);
  background: var(--text-on-dark);
}

.cnl .cnl-nav__drawer { display: none; }

/* --------------------------------------------------------------------------
   Hero
   -------------------------------------------------------------------------- */

.cnl .cnl-hero {
  position: relative;
  overflow: hidden;
  background: var(--text-primary);
  /* Was 900px, which held 428px of content and 472px of slack: a full
     viewport of damson before a visitor reached anything they could act on.
     760 is 15% off and still leaves 166px of air above and below the copy,
     so nothing is crowded and the ring plane keeps room to read as a plane.
     Paired with moving the demo up the page, the product is now reachable
     without a deliberate scroll. */
  min-height: 760px;
  display: flex;
  align-items: center;
}

/* The ring plane is painted into this layer by canoli-rings.js. With no
   JavaScript it stays empty and the hero is a plain Damson field, which
   still carries the whole argument. */
.cnl .cnl-hero__rings {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.cnl .cnl-hero__rings svg { display: block; }

/* The ring colour is set here rather than by the script: a custom property
   does not resolve inside an SVG presentation attribute, so stroke="var(..)"
   in JavaScript paints nothing at all. Keeping it in CSS also means the
   rings follow the brand palette instead of a value frozen into a script. */
.cnl .cnl-hero__rings g { stroke: var(--brand-terracotta); fill: none; }

/* WCAG 2.2.2 pause control. Discreet, but a real button with a real label:
   the field grows indefinitely and cannot settle on its own. */
.cnl .cnl-motion-toggle {
  position: absolute;
  right: var(--cnl-gut);
  bottom: 24px;
  z-index: 4;
  /* Square and icon-only. 36px clears the 24px WCAG 2.5.8 target minimum
     with room to spare, which matters more here than it did for a text pill
     because there is no longer a word to aim at. The icon inherits
     currentColor, so the hover rule below still moves it. */
  width: 36px;
  height: 36px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--cnl-control-edge);
  border-radius: var(--radius-button);
  background: color-mix(in srgb, var(--text-primary) 78%, transparent);
  color: var(--text-on-dark-subtle);
  cursor: pointer;
}
.cnl .cnl-motion-toggle:hover { color: var(--text-on-dark); }
.cnl .cnl-motion-toggle:focus-visible { outline: 2px solid var(--cnl-focus); outline-offset: 2px; }

/* The rings reach the top edge at full strength, and the nav above is flat
   ink, so without this they meet in a hard horizontal line. */
.cnl .cnl-hero__seam {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 150px;
  background: linear-gradient(
    to bottom,
    var(--text-primary) 0%,
    color-mix(in srgb, var(--text-primary) 62%, transparent) 45%,
    transparent 100%);
  pointer-events: none;
}

/* Horizontal scrim: the copy occupies the left third on desktop, so the
   protection runs along that axis. Mobile re-points it vertically, since
   the copy stacks from the top there and a horizontal ramp would guard
   the wrong axis entirely. */
/* TWO LAYERS, and the second one exists because the first cannot reach.

   The left-to-right ramp is unchanged: 97% at the left edge, 80% at 42%, gone
   by 72%. It is what makes the headline readable and it is tuned for that.

   The station column sits at 64% to 96% of the viewport, so it STRADDLES the
   point where that ramp is designed to vanish. Measured with the rings at the
   top of their cycle (canoli-rings.js animates group opacity 0.26 to 0.94),
   station text crossing a ring stroke reads 1.78:1 muted and 3.04:1 bright,
   against the 3:1 WCAG asks of large text. 59 of the 89 ring elements overlap
   that block.

   Pushing the linear ramp further right is the obvious fix and it is the
   wrong one: to add alpha at 72% it has to add more at 50%, and 50% is
   exactly where the ring field is centred. Adam's constraint, and he is
   right, is that the middle of the rings must not go darker.

   So the second layer is a RADIAL centred on the station column at 82%, not a
   panel. It reaches full strength where the text is, falls to nothing by 62%
   of its radius, and is zero long before it reaches the rings' centre. Radial
   because the brief also ruled out a hard-edged block over the rings, and a
   gradient with no stop boundary has no edge to see: the only thing that
   changes is how bright the strokes are allowed to burn immediately behind
   four lines of type.

   0.34 is not a round number chosen by eye. It is the alpha at which muted
   station text clears 3:1 over a ring at peak opacity, computed rather than
   sampled, with a little over it so the figure holds as the rings animate. */
.cnl .cnl-hero__scrim {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(
      ellipse 46% 58% at 82% 50%,
      color-mix(in srgb, var(--text-primary) 38%, transparent) 0%,
      color-mix(in srgb, var(--text-primary) 30%, transparent) 38%,
      transparent 66%),
    linear-gradient(
      to right,
      color-mix(in srgb, var(--text-primary) 97%, transparent) 0%,
      color-mix(in srgb, var(--text-primary) 80%, transparent) 42%,
      transparent 72%);
  pointer-events: none;
}

.cnl .cnl-hero__inner {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: calc(var(--cnl-max) + var(--cnl-gut) * 2);
  margin: 0 auto;
  padding: 0 var(--cnl-gut);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 64px;
}

/* Copy and stations each centre on their own half of the page, so the two
   read as a balanced pair rather than one hugging a margin. */
.cnl .cnl-hero__copy { flex: 0 0 560px; max-width: 560px; }

.cnl .cnl-hero__headline {
  font-size: 56px;
  line-height: 1.0;
  letter-spacing: -0.022em;
  color: var(--text-on-dark);
  margin: 20px 0 0;
}

.cnl .cnl-hero__lede {
  font-size: 17px;
  line-height: 1.64;
  color: var(--text-on-dark-subtle);
  margin-top: 22px;
}

.cnl .cnl-hero__ctas { display: flex; gap: 12px; margin-top: 26px; flex-wrap: wrap; }

.cnl .cnl-hero__note {
  font-size: 14px;
  color: var(--text-on-dark-subtle);
  margin-top: 22px;
}

/* Stations sit at the ring origin: the station that lights and the ring
   being born are the same event. */
.cnl .cnl-stations {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 22px;
  position: relative;
}

/* A soft shadow, not a plate. A planet casts one across its own rings, so
   the device is borrowed from the reference rather than invented to solve
   a contrast problem.

   closest-side, NOT the default farthest-corner. A farthest-corner ellipse is
   sized to reach the box's corners, which leaves it still ~70% opaque where it
   meets the middle of each side, and the element boundary then cuts it off
   flat: that is the hard edge. closest-side lands the transparent stop exactly
   on the sides, so the fade completes inside the box on every axis.

   The stops are darker and run longer than the farthest-corner version's to
   compensate. Since closest-side shrinks the ellipse, the same stops would
   have reached transparent sooner and thinned the pool over the lettering.
   84/62/0 holds the alpha behind the text within a point or two of what it
   was, which is the part that was carrying legibility over a moving ring. */
.cnl .cnl-stations::before {
  content: "";
  position: absolute;
  inset: -150px -160px;
  background: radial-gradient(
    ellipse closest-side at center,
    color-mix(in srgb, var(--text-primary) 84%, transparent) 0%,
    color-mix(in srgb, var(--text-primary) 62%, transparent) 75%,
    transparent 100%);
  z-index: -1;
  pointer-events: none;
}

.cnl .cnl-station { display: flex; align-items: center; gap: 20px; }

.cnl .cnl-station__n {
  font-family: var(--font-mono);
  font-size: 26px;
  color: var(--text-on-dark-subtle);
}

/* EVERY label is laid out at the ACTIVE size and weight; the inactive ones are
   shrunk with a transform, which costs no layout.

   This is not a stylistic choice. When the active label carried a larger
   font-size, the widest ROW changed as the highlight moved, the list hugs its
   content, and so the whole block slid 45px sideways once per station. Row
   HEIGHT was already safe by luck: exactly one row is active at any moment, so
   the column total is the same whichever one it is. Width is a max(), not a
   sum, and max() has no such symmetry.

   Laying every label out at 40/700 makes that max() constant by construction,
   with no pinned pixel width to go stale if the face falls back. The
   transform also gives the highlight something to animate, which a font-size
   swap could only jump between. inline-block is required: transforms do not
   apply to non-replaced inline elements. */
.cnl .cnl-station__label {
  display: inline-block;
  font-size: 40px;
  font-weight: 700;
  color: var(--text-on-dark-subtle);
  line-height: 1.1;
  transform: scale(0.9);
  transform-origin: left center;
  transition: transform 0.32s ease, color 0.32s ease;
}

.cnl .cnl-station--active .cnl-station__n { color: var(--brand-gold); }
.cnl .cnl-station--active .cnl-station__label {
  color: var(--text-on-dark);
  transform: none;
}

/* --------------------------------------------------------------------------
   Live counters
   -------------------------------------------------------------------------- */

.cnl .cnl-counters {
  background: var(--ink-deep);
  padding: 40px var(--cnl-gut);
}

.cnl .cnl-counters__grid {
  max-width: var(--cnl-max);
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
}

.cnl .cnl-counter { display: flex; flex-direction: column-reverse; gap: 8px; }

.cnl .cnl-counter__value {
  font-family: var(--font-mono);
  font-size: 34px;
  color: var(--text-on-dark);
  line-height: 1;
  display: flex;
  align-items: baseline;
  gap: 2px;
}

.cnl .cnl-counter__unit { font-size: 16px; color: var(--brand-gold); }

.cnl .cnl-counter__label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-on-dark-subtle);
}

/* --------------------------------------------------------------------------
   Why now
   -------------------------------------------------------------------------- */

/* HOLDING GROUND, not the final one. Adam is taking this section into Figma.

   It was accent/punch, and punch was right when the band held one ticking
   number and one sentence: a fully saturated field is a good place to make a
   single loud statement. It now carries six rows with three levels of
   hierarchy each, and density and saturation pull against each other. The
   contrast was never the problem, the quietest line measured 6.97:1 on punch,
   it is that the section changed job and kept its clothes.

   surface-muted rather than shell or white, so the band still reads as its
   own thing between two damson sections rather than merging with the other
   light sections above and below it. It also breaks up what a reviewer called
   three consecutive dark bands reading as one field.

   The focus token above already resolves to --text-primary for this section,
   so the ring was correct on punch and stays correct here. */
.cnl .cnl-why-now {
  background: var(--bg-muted);
  padding: 100px var(--cnl-gut);
}

/* 900 was right for a headline over a list of prose rows. The chain and the
   tile strip below are full-width blocks, and holding them to 900 left the
   band looking like it stopped two thirds of the way across while every other
   section ran to the grid. */
.cnl .cnl-why-now__inner { max-width: var(--cnl-max); margin: 0 auto; }

/* THE TIMELINE ROWS ARE GONE, and so is the sources paragraph they sat above.

   What replaced them, and why. The rows were six dated entries of prose at
   13px to 15px, about 450 words. Every fact was correct and none of it was
   read: the argument the section exists to make, that a rule binding someone
   else still lands on your recipe, had to be reconstructed by the reader from
   a list of legislation. Adam, looking at it: "This is visually a block of
   text and doesn't explain itself."

   So the argument is now a chain of three panels and the dates are demoted to
   evidence you can open one at a time. The dates did not get less true, they
   got less loud, which is the right weight for supporting material.
   -------------------------------------------------------------------------- */

/* THE CHAIN. Rule, then buyer, then you.
   ---------------------------------------------------------------------------
   Three panels reading left to right with a chevron between them, ending on a
   dark slab. The last panel is the only one that changes colour because it is
   the only one about the reader: the chain visibly lands on them, which is the
   whole claim of the band. */
.cnl .cnl-chain {
  list-style: none;
  margin: 40px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 30px;
  text-align: left;
}

.cnl .cnl-chain__step {
  position: relative;
  background: var(--bg-primary);
  border-radius: 16px;
  padding: 28px 26px;
}

/* The chevron sits in the gap rather than inside either panel, so neither
   panel has to reserve space for it and the two stay the same width. Two
   borders rotated 45deg is the cheapest honest arrow: no asset, no font, and
   it inherits currentColor. */
.cnl .cnl-chain__step + .cnl-chain__step::before {
  content: "";
  position: absolute;
  left: -21px;
  top: 50%;
  width: 9px;
  height: 9px;
  border-right: 2px solid var(--decorative-muted);
  border-top: 2px solid var(--decorative-muted);
  transform: translateY(-50%) rotate(45deg);
}

.cnl .cnl-chain__step--lands {
  background: var(--bg-sidebar);
}

/* The eyebrow is a butter pill on every panel, light and dark alike, because
   butter is a light plate either way and the label stays ink on it. */
.cnl .cnl-chain__eyebrow {
  display: inline-block;
  margin: 0;
  padding: 5px 10px;
  border-radius: 999px;
  background: var(--brand-gold);
  color: var(--text-primary);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.4;
}

.cnl .cnl-chain__title {
  margin: 12px 0 0;
  font-size: 20px;
  line-height: 1.2;
  letter-spacing: -0.008em;
  color: var(--text-primary);
}

.cnl .cnl-chain__body {
  margin: 12px 0 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--decorative-muted);
}

.cnl .cnl-chain__step--lands .cnl-chain__title { color: var(--text-on-dark); }
.cnl .cnl-chain__step--lands .cnl-chain__body {
  color: color-mix(in srgb, var(--text-on-dark) 78%, transparent);
}

/* THE DATES, as evidence.
   -------------------------------------------------------------------------- */

.cnl .cnl-rules {
  margin-top: 56px;
  text-align: left;
  /* The gap the tile grid uses, named once so the pointer below can do
     arithmetic against it instead of assuming the tiles are exact fractions
     of the strip. --rules-n comes from the data, via the markup. */
  --rules-gap: 14px;
}

.cnl .cnl-rules__head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}

/* .cnl-eyebrow is butter, which is correct on the dark bands it was written
   for and close to invisible on this parchment one. The eyebrow keeps the
   class for its size and tracking and overrides the colour. */
.cnl .cnl-rules__eyebrow { margin: 0; color: var(--decorative-muted); }

/* A strip of tiles that open panels has to say so. Without this line the
   affordance is carried by nothing but the ring on one tile. */
.cnl .cnl-rules__hint {
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--decorative-muted);
}

.cnl .cnl-rules__tiles {
  list-style: none;
  margin: 22px 0 0;
  padding: 0;
  display: grid;
  /* Sized from the data via --rules-n (set on .cnl-rules by the markup),
     not a literal: nine tiles as of 2026-09-04, six before. */
  grid-template-columns: repeat(var(--rules-n, 6), minmax(0, 1fr));
  gap: 14px;
}

.cnl .cnl-rules__tile { display: flex; flex-direction: column; gap: 8px; }

/* THE TILE. A year over a month, shaped like a calendar block.
   ---------------------------------------------------------------------------
   The day is deliberately not on the face. Four of the six have one and two do
   not, so a day on the tile would either leave gaps or invite one to be
   invented. The exact date is in the panel kicker, one tap away, where it can
   be stated in full. */
.cnl .cnl-daytile {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  border-radius: 14px;
  background: var(--bg-surface);
  overflow: hidden;
  cursor: pointer;
  text-align: center;
  font: inherit;
  transition: box-shadow 0.16s ease, transform 0.16s ease;
}

.cnl .cnl-daytile:hover { transform: translateY(-2px); }

/* THE YEAR IS HALF THE DATE, NOT A CAPTION.
   ---------------------------------------------------------------------------
   It shipped at 10px here and 9px on phones, against a month set at 40 and 30.
   Adam, reading the page on a phone: "My eyes aren't great and I couldn't
   focus well enough to read the year labels."

   That size was chosen as if the strip were a label above a value, which it is
   not: "2026" over "OCT" is one date split across two lines, and a reader
   deciding whether a rule lands this year or in two needs both halves. 9px is
   also simply too small for a figure anyone has to focus on, whatever the
   hierarchy argument.

   14px keeps the month clearly dominant while making the year legible, and it
   is on the documented ramp. The cap grows about 4px, which the tile absorbs.
   Same size at both widths: the phone is where this was unreadable, so it is
   the last place to shrink it. */
.cnl .cnl-daytile__year {
  display: block;
  padding: 8px 0;
  background: var(--brand-terracotta);
  color: var(--text-primary);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.07em;
}

.cnl .cnl-daytile__month {
  display: block;
  padding: 16px 0 6px;
  font-family: var(--font-display);
  font-size: 40px;
  font-weight: 700;
  line-height: 1;
  color: var(--text-primary);
}

/* A rule already in force is spent: the cap loses the accent and the month
   drops to the muted rung. The plate stays white, because using the band's own
   ground made the tile look unrendered rather than finished. */
.cnl .cnl-daytile--in-force .cnl-daytile__year {
  background: var(--decorative-muted);
  color: var(--text-on-dark);
}
.cnl .cnl-daytile--in-force .cnl-daytile__month { color: var(--decorative-muted); }

/* INSIDE, not outside. An outside ring draws at the fill's radius plus its own
   width, so the corners showed a sliver of the band between the two arcs and
   read as damage. An inside stroke follows the same path as the shape. */
.cnl .cnl-daytile.is-open {
  box-shadow: inset 0 0 0 2px var(--decorative-muted);
}

/* The territory tag on the tile face, under the month. Same size and
   tracking as the state line below the tile, so the face reads year,
   month, where. */
.cnl .cnl-daytile__territory {
  display: block;
  padding: 0 4px 16px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--decorative-muted);
}
.cnl .cnl-daytile__state {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: center;
  color: var(--decorative-muted);
}

/* The imminent rule is the only one that gets colour, so urgency reads as a
   difference rather than as decoration. --link-on-surface rather than a
   semantic fail colour: this is a deadline, not a verdict, and the fail rung
   is reserved for the compliance results in the demo card. */
.cnl .cnl-rules__tile:has(.cnl-daytile--next) .cnl-daytile__state {
  color: var(--link-on-surface);
}

.cnl .cnl-rules__detail { margin-top: 22px; }

.cnl .cnl-rule {
  position: relative;
  background: var(--bg-surface);
  border-radius: 14px;
  padding: 24px 26px;
}

/* THE POINTER TIES THE PANEL TO THE TILE THAT OPENED IT.
   ---------------------------------------------------------------------------
   Without it the callout is a card that changes content when you press
   something above it, and nothing on screen says which of the six did it.

   Drawn as a border triangle in the panel's own colour rather than an extra
   node, so it cannot drift out of step with the panel the way a positioned
   element does. --notch-x is set by Alpine from the open index, because only
   the client knows which tile that is; the 50% fallback keeps it centred and
   harmless if the script never runs. */
.cnl .cnl-rule::before {
  content: "";
  position: absolute;
  top: -9px;
  /* The open tile's centre, exactly. A tile is the strip minus its gaps
     divided by the count, and tile i starts i of those plus i gaps along.
     The percentage resolves against this panel, which spans the same width
     as the strip, so the two agree by construction rather than by luck.

     50% is the fallback for no script: centred, which is wrong for five of
     the six but never points at the wrong tile confidently. */
  left: calc(
    var(--notch-i, 0) * (
      (100% - (var(--rules-n, 6) - 1) * var(--rules-gap)) / var(--rules-n, 6)
      + var(--rules-gap)
    )
    + ((100% - (var(--rules-n, 6) - 1) * var(--rules-gap)) / var(--rules-n, 6)) / 2
  );
  transform: translateX(-50%);
  width: 0;
  height: 0;
  border-left: 10px solid transparent;
  border-right: 10px solid transparent;
  border-bottom: 10px solid var(--bg-surface);
}

.cnl .cnl-rule__kicker {
  margin: 0;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--decorative-muted);
}

/* Plain English, not the statutory title. "The Food (Promotion and Placement)
   (Scotland) Regulations 2025" tells a food developer nothing they can act on,
   and the SI number is already on the source line below. */
.cnl .cnl-rule__title {
  margin: 8px 0 0;
  font-size: 20px;
  line-height: 1.2;
  color: var(--text-primary);
}

.cnl .cnl-rule__body {
  margin: 8px 0 0;
  font-size: 16px;
  line-height: 1.5;
  color: var(--text-primary);
}

/* WHO IT BINDS, and the qualifiers that come with it. Kept from the list this
   replaced, and still the honesty mechanism: most of these bind retailers or
   large advertisers rather than the reader, and two carry qualifiers that
   change who is caught at all. A panel that named the rule and left the reader
   to assume it was theirs would be the same false claim the countdown made. */
.cnl .cnl-rule__who {
  margin: 8px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--decorative-muted);
}

.cnl .cnl-rule__source {
  margin: 8px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--decorative-muted);
}

.cnl .cnl-why-now__headline {
  font-size: 42px;
  line-height: 1.12;
  letter-spacing: -0.02em;
  color: var(--text-primary);
}

.cnl .cnl-why-now__body {
  font-size: 17px;
  line-height: 1.62;
  color: var(--text-primary);
  margin-top: 18px;
}


/* --------------------------------------------------------------------------
   Why this exists
   -------------------------------------------------------------------------- */

.cnl .cnl-bench {
  position: relative;
  background: var(--text-primary);
  /* 620 tall on the frame: the copy sits centred beside the full-bleed
     photo, which needs the height to keep the hands and the bench. */
  min-height: 620px;
  display: flex;
  align-items: center;
  padding: 72px var(--cnl-gut) 76px;
}
/* The copy keeps out of the photo's third: 900px of headline against a
   photo that starts at 960px on a 1440 frame. */
.cnl .cnl-bench__copy { max-width: 860px; }
/* The photo (Figma 915:204): the right third of the section, top to
   bottom, no card. The image's shadows are already the ink token, so its
   left edge is soft; the crop anchors to the top so the hands stay whole. */
.cnl .cnl-bench__photo {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 33.333%;
  margin: 0;
  overflow: hidden;
}
/* Specificity matches the global `.marketing-page img:not(...)` reset
   (0,3,1), which forces height:auto; this sheet loads after it, so the
   equal-specificity rule here wins and the photo can fill its box. */
.marketing-page .cnl .cnl-bench__photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}

/* The inner box reserves the photo's third plus a 24px gap at every width
   above the phone break, so the copy can never run under the picture. The
   percentage is of the inner box, which is narrower than the section by two
   gutters, so this reserves slightly more than the photo takes: a safe
   over-estimate. At 1440 the copy still gets its 860px. */
.cnl .cnl-bench__inner {
  width: 100%;
  max-width: var(--cnl-max);
  margin: 0 auto;
  padding-right: calc(33.333% + 24px);
}

.cnl .cnl-bench__headline {
  font-size: 46px;
  line-height: 1.06;
  letter-spacing: -0.02em;
  color: var(--text-on-dark);
  margin-top: 14px;
}

.cnl .cnl-bench__body {
  font-size: 17px;
  line-height: 1.62;
  color: var(--text-on-dark-subtle);
  margin-top: 20px;
  max-width: 640px;
}

/* The turn: the section states a problem, then answers it. Mint because
   immutable version history is an evidence-integrity property, not a
   "saved" state. See --mint-on-dark, which is deliberately NOT the
   compliance green. */
/* The section's payoff, and it has to look like one.
   ---------------------------------------------------------------------------
   This was 17px, IDENTICAL to the body above it in both size and weight, so
   the only thing separating the conclusion from the setup was hue. A reader
   skimming saw two paragraphs of equal weight, one of which happened to be
   green. Mint is already doing enough work here: the token is restricted to
   dark grounds because it fails on cream.

   The frame (Figma 915:209 / 982:359, 2026-09-08) sets it in the display
   face at 26px bold, line-height 1.2, 22px on phones: a second, smaller
   headline in mint rather than a coloured paragraph. It sits well under the
   46px title, so it reads as the conclusion without competing. */
.cnl .cnl-bench__turn {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 26px;
  line-height: 1.2;
  color: var(--mint-on-dark);
  margin-top: 16px;
  max-width: 640px;
}

/* The marker rides the claim it qualifies. aria-hidden on both this and the
   one in the note: an asterisk read aloud as "star" twice is noise, and the
   footnote's own sentence carries the meaning for a screen reader without it. */
.cnl .cnl-bench__mark {
  margin-left: 2px;
  font-size: 0.8em;
  vertical-align: super;
  line-height: 0;
}

/* Deliberately quiet, and deliberately NOT hidden. The claim above is the
   section's emotional turn and is not true on every tier, so the
   qualification has to be legible rather than technically present.
   --text-on-dark-subtle on damson measures 9.39:1 at 14px. */
.cnl .cnl-bench__note {
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-on-dark-subtle);
  margin-top: 12px;
  max-width: 640px;
}

/* --------------------------------------------------------------------------
   What one change does: the live metric strip
   --------------------------------------------------------------------------
   Damson, continuing the bench section above it rather than opening a fourth
   light band. The card inside is a light surface on purpose: the strip's
   tiles pair a tinted background with its matching -text token, and those
   tints are built for a light ground. On damson they would go to mud.

   No kicker. The headline carries it.
   -------------------------------------------------------------------------- */

.cnl .cnl-demo {
  background: var(--text-primary);
  /* Top padding, which this section did not used to need. It sat under the
     bench section, whose own bottom padding held it off. It now sits directly
     under the counters strip, and the strip is a different dark (--ink-deep
     against this section's damson), so without a gap the card's white edge
     lands a few pixels under a colour change and the two read as one band. */
  padding: 88px var(--cnl-gut) 96px;
}

/* Copy left, the card right, which is the arrangement the substrate and
   security sections already use. Left-aligned in one column left roughly 650px
   of empty damson beside a 720px card at desktop width. */
.cnl .cnl-demo__inner {
  max-width: var(--cnl-max);
  margin: 0 auto;
  display: grid;
  /* Two children: the copy block and the card. The card takes the larger
     share because it is the subject of the section. */
  grid-template-columns: 380px 1fr;
  gap: 72px;
  align-items: center;
}

.cnl .cnl-demo__copy { align-self: center; }

.cnl .cnl-demo__headline {
  font-size: 42px;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--text-on-dark);
}

.cnl .cnl-demo__lede {
  font-size: 17px;
  line-height: 1.6;
  color: var(--text-on-dark-subtle);
  margin-top: 16px;
}

/* The card carries the same butter border the featured pricing card wears,
   and for the same reason: it is the one card on the page a visitor is meant
   to touch. The border is also what makes the badge read as a tab attached to
   the card rather than a chip floating above it, because the pill and the
   border are the same butter and the line appears to run into it.

   position: relative for the badge, and explicitly NOT overflow:hidden: the
   badge straddles the top edge by half its height, and a clipping card cuts
   it in two. That is exactly what happened to the pricing badge. */
.cnl .cnl-demo__card {
  position: relative;
  background: var(--bg-surface);
  border: 2px solid var(--brand-gold);
  border-radius: var(--radius-card);
  padding: 28px 32px 24px;
}

/* Out of flow. In the flow it would take a row of its own and push the recipe
   name and everything under it down. */
.cnl .cnl-demo__badge {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 5px 12px;
  border-radius: var(--radius-chip);
  background: var(--brand-gold);
  color: var(--text-primary);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.cnl .cnl-demo__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--cnl-hairline-dark);
}

.cnl .cnl-demo__recipe {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 600;
  color: var(--text-primary);
}

/* Says "illustrative" in the component itself, not in a footnote someone can
   scroll past. The numbers are shaped like real output and are not real. */
.cnl .cnl-demo__basis {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--decorative-muted);
}

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

/* Name, slider, amount. Three columns rather than the old two, because the
   amounts became editable: the numbers on this card are now produced by the
   real engine from whatever the visitor sets, so the row has to offer a way
   to set it. The slider column takes the slack so the name and the figure
   stay put while it resizes. */
.cnl .cnl-demo__row {
  display: grid;
  grid-template-columns: 132px 1fr 68px;
  align-items: center;
  gap: 16px;
  padding: 7px 0;
  font-size: 15px;
  color: var(--text-deep);
  border-bottom: 1px solid var(--border-divider);
}

.cnl .cnl-demo__name {
  cursor: pointer;
  /* The label is the slider's accessible name and its second hit area, so it
     gets the full row height rather than just the text's. */
  padding: 4px 0;
}

/* The amount control.
   ---------------------------------------------------------------------------
   A number input would be more precise and would invite nobody. The point of
   this card is that a visitor discovers the relationship between an amount
   and a verdict, and discovery needs something that asks to be dragged.

   appearance:none removes the platform track and thumb on every engine, so
   the two have to be rebuilt per engine below. There is no shorthand that
   covers WebKit and Gecko together, hence the duplication. */
.cnl .cnl-demo__slider {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  /* 24px of box against a 4px visible track. WCAG 2.5.8 asks 24x24 of the
     TARGET, not of the paint, and a 4px-tall hit area on a touch screen is
     the difference between a demo and a fight. */
  height: 24px;
  margin: 0;
  background: transparent;
  cursor: pointer;
}

.cnl .cnl-demo__slider::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: var(--radius-button);
  background: var(--border-divider);
}
.cnl .cnl-demo__slider::-moz-range-track {
  height: 4px;
  border-radius: var(--radius-button);
  background: var(--border-divider);
}

/* Damson, not gold. 1.4.11 asks 3:1 of a control's visual boundary and gold
   on this cream card measures under it; damson reads 16.92:1, and the gold
   is already carrying the edited-row marker two lines away, where it means
   something else. */
.cnl .cnl-demo__slider::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  margin-top: -6px;              /* centres the 16px thumb on the 4px track */
  border: 0;
  border-radius: var(--radius-button);
  background: var(--text-primary);
}
.cnl .cnl-demo__slider::-moz-range-thumb {
  width: 16px;
  height: 16px;
  border: 0;
  border-radius: var(--radius-button);
  background: var(--text-primary);
}

.cnl .cnl-demo__slider:focus-visible {
  outline: 2px solid var(--cnl-focus);
  outline-offset: 2px;
  border-radius: var(--radius-button);
}

/* While a recalculation is in flight the figures are, briefly, for the
   previous recipe. A spinner would be a lie about how long that is (usually
   under 40ms); a slight fade says "settling" without claiming a wait. It only
   appears at all if the response takes over 250ms. */
.cnl .cnl-demo__card--busy .cnl-metrics,
.cnl .cnl-demo__card--busy .cnl-cost { opacity: 0.55; }
.cnl .cnl-metrics,
.cnl .cnl-cost { transition: opacity 0.18s ease; }

.cnl .cnl-demo__row:last-child { border-bottom: 0; }

/* The edited row. A 2px left rule would be the cheap version and the craft
   floor rules it out; the mark is the row's own ground plus the gold dot,
   which is the same marker the app rail already uses. */
.cnl .cnl-demo__row--changed {
  position: relative;
  font-weight: 600;
  color: var(--text-primary);
}

.cnl .cnl-demo__row--changed::before {
  content: "";
  position: absolute;
  left: -18px;
  top: 50%;
  width: 7px;
  height: 7px;
  margin-top: -3px;
  border-radius: var(--radius-button);
  background: var(--brand-gold);
}

/* Measured values are mono and tabular, so a digit changing does not shift
   the column. Browser default numerals are proportional and would jitter. */
.cnl .cnl-demo__qty {
  font-family: var(--font-mono);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  color: var(--text-deep);
}

.cnl .cnl-metrics {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 8px;
  margin: 20px 0 0;
}

/* Four, since cost left. It could not be made real for a logged-out visitor
   and a card of true numbers with one invented one in it is not a card of
   true numbers. */
.cnl .cnl-metrics--four { grid-template-columns: repeat(4, 1fr); }

.cnl .cnl-metric {
  background: var(--bg-muted);
  border-radius: var(--radius-card-sm);
  padding: 12px 12px 10px;
}

/* THE NUTRITION TILES ARE OUTLINED, THE VERDICT TILES ARE FILLED, and the
   difference is the point rather than a style choice.

   A filled tile on this card means something: the verdicts use a semantic
   wash, pink for a fail, so a reader can see the result before reading a
   word. Giving the four nutrition figures the same filled treatment in a
   different colour spent that signal on numbers that carry no verdict, and
   put seven filled boxes on one card competing with each other.

   Scoped to --four deliberately. The base rule still fills, because
   --withheld and --nutri rely on it, and a border on the base would put one
   on the verdict tiles too, which the comp does not have. */
.cnl .cnl-metrics--four .cnl-metric {
  background: transparent;
  border: 1px solid var(--border-divider);
}
/* The cost line. One row between the nutrient tiles and the verdicts,
   drawn like a tile (same hairline, same radius, same label) but a single
   line tall. A fifth tile would be a third row of tiles on a phone; see the
   mobile block for the height budget that rules that out. */
.cnl .cnl-cost {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 8px 0 0;
  padding: 7px 14px;
  border: 1px solid var(--border-divider);
  border-radius: var(--radius-card-sm);
}
.cnl .cnl-cost__value {
  display: flex;
  align-items: baseline;
  gap: 4px;
  margin: 0;
  font-family: var(--font-mono);
  font-size: 16px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}

.cnl .cnl-metric__label {
  font-family: var(--font-primary);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--decorative-muted);
}

.cnl .cnl-metric__value {
  display: flex;
  align-items: baseline;
  gap: 3px;
  margin: 5px 0 0;
  font-family: var(--font-mono);
  font-size: 21px;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
  transition: color 0.2s ease;
}

/* --text-secondary, not --decorative-muted: at 12px this is small text and
   the tertiary rung measures 4.4:1 on the tile's surface, just under AA.
   tokens-canoli.css says it outright: prose has no lighter step than the
   secondary rung that still clears AA. */
.cnl .cnl-metric__unit {
  font-size: 12px;
  color: var(--text-secondary);
}
.cnl .cnl-metric__unit--pre { font-size: 15px; color: var(--text-primary); }

/* The verdict tile carries the news in its colour, which is the whole idea
   of the component. Tint plus its matching -text token, never the identity
   colour as a label. */
.cnl .cnl-metric--pass { background: var(--status-pass-bg); }
.cnl .cnl-metric--pass .cnl-metric__value { color: var(--status-pass-text); }
.cnl .cnl-metric--pass .cnl-metric__label { color: var(--status-pass-text); }

.cnl .cnl-metric--fail { background: var(--status-fail-bg); }
.cnl .cnl-metric--fail .cnl-metric__value { color: var(--status-fail-text); }
.cnl .cnl-metric--fail .cnl-metric__label { color: var(--status-fail-text); }

/* A withheld verdict is neither pass nor fail and must borrow neither
   colour. NPM 2018 reads free sugars, and 17% of the library is still
   unclassified for them, so this state is reachable in production rather
   than defensive. Painting it green would be a lie and painting it red would
   be a different one; it takes the card's own muted ground and says so in
   words instead. */
.cnl .cnl-metric--withheld { background: var(--bg-muted); }
.cnl .cnl-metric--withheld .cnl-metric__value,
.cnl .cnl-metric--withheld .cnl-metric__label { color: var(--decorative-muted); }
/* The withheld note is 12px small text, and the muted rung measures 4.4:1
   on this ground, just under AA. The secondary rung is the lightest that
   clears it (tokens-canoli.css says so), and it is still quieter than the
   17px verdict word above it. */
.cnl .cnl-metric--withheld .cnl-metric__unit { color: var(--text-secondary); }

.cnl .cnl-metric--verdict .cnl-metric__value { font-size: 17px; }
/* Full opacity, not 0.72: at 12px the unit is small text and needs 4.5:1.
   Blended at 0.72 the pass-text green over the pass tint measured 3.81:1
   and failed the audit, and any blend at all is a guess the audit can
   catch again. The size difference already makes the note read as
   secondary to the verdict word. */
.cnl .cnl-metric--verdict .cnl-metric__unit { color: inherit; }
.cnl .cnl-metric--verdict.cnl-metric--withheld .cnl-metric__unit { color: var(--text-secondary); }

/* The two verdicts get their own full-width band under the nutrients.
   Carrying both models is the differentiator, so it is not a fifth tile
   squeezed in beside four measurements; it is the conclusion the
   measurements lead to, and it reads as one. */
.cnl .cnl-metrics--verdicts {
  grid-template-columns: repeat(3, 1fr);
  margin-top: 8px;
}

/* NUTRI-SCORE uses the official artwork.
   ---------------------------------------------------------------------------
   This was a letter in a chip, coloured with the scheme's five hexes. It
   worked and it was worth less: Nutri-Score is a registered scheme with a
   fixed lockup, and a version we drew ourselves is an approximation on a page
   whose whole argument is that our numbers are checkable. The real marks live
   in /img/nutriscore, including ns-none for the withheld case, so the tile
   never invents a grade.

   That also deleted the five hardcoded scheme colours, which is a better
   outcome than the pragma they needed.

   IT KEEPS THE TILE GROUND. I removed it first, reasoning that the mark
   carries its own colour and a box would fight it. Adam looked at the result
   and disagreed, and he is right: the three verdicts are one band, and
   stripping the ground from the third made it read as a caption floating
   beside two tiles rather than the third member of a set. The artwork sits on
   the same muted ground as its neighbours.

   No score is printed beside it either. The mark already says A to E, which
   is the entire point of a front-of-pack scheme, and the raw points mean
   nothing to a reader who does not already know the model. It also pushed the
   tile taller than the two beside it. */
.cnl .cnl-metric--nutri { background: var(--bg-muted); }

/* max-height, NOT height. marketing.css forces height:auto on every image via
   `.marketing-page img:not(.nav__logo-img):not(.footer__logo-img)`, which at
   (0,3,1) beats any sane class selector here; a height rule was silently
   ignored and the mark rendered at its natural 96px, twice the size intended
   and overpowering the two HFSS tiles beside it. max-height sits alongside
   height:auto rather than fighting it, which is the same resolution the
   customer logo wall reached. */
.cnl .cnl-nutri__mark {
  display: block;
  width: auto;
  max-height: 46px;
  margin-top: 2px;
}


/* Two words and a year have to fit on one line at 1440 and at 390, and the
   label is what tells a reader these are two different models rather than a
   duplicated tile, so it may not truncate. */
.cnl .cnl-metrics--verdicts .cnl-metric__label {
  white-space: nowrap;
  letter-spacing: 0.06em;
}

.cnl .cnl-demo__foot {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--border-divider);
}

/* OUTLINE, NOT FILL, to match the comp.
   ---------------------------------------------------------------------------
   It shipped as a filled muted pill with a 12% hairline, which read as a
   disabled control on a white card: the one thing in the section a visitor is
   invited to press looked like the one thing they could not. A solid ink
   outline on the card's own white reads as a button, and it is 15.9:1 against
   the card so the boundary clears 1.4.11 with room. The fill moves to hover,
   where a background change is a response rather than a resting state. */
.cnl .cnl-demo__swap {
  flex: 0 0 auto;
  font-family: var(--font-primary);
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  background: transparent;
  border: 1px solid var(--text-primary);
  border-radius: var(--radius-button);
  padding: 9px 16px;
  cursor: pointer;
  transition: background-color 0.15s ease;
}
.cnl .cnl-demo__swap:hover { background: var(--bg-muted); }

/* Quieter than the swap on purpose: getting back is a safety net, not an
   invitation. It only exists once something has actually been changed, so it
   never occupies the row while the card is untouched. */
.cnl .cnl-demo__reset {
  flex: 0 0 auto;
  font-family: var(--font-primary);
  font-size: 14px;
  font-weight: 500;
  color: var(--decorative-muted);
  background: none;
  border: 0;
  padding: 9px 4px;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.cnl .cnl-demo__reset:hover { color: var(--text-primary); }
.cnl .cnl-demo__reset:focus-visible {
  outline: 2px solid var(--cnl-focus);
  outline-offset: 2px;
  border-radius: var(--radius-button);
}

/* The second lede line carries the claim that these figures are real, so it
   is set below the first rather than folded into it: a reader skimming the
   headline should be able to stop on that sentence alone. */
.cnl .cnl-demo__lede--sub {
  font-size: 15px;
  color: var(--text-on-dark-muted);
  margin-top: 12px;
}
.cnl .cnl-demo__swap:focus-visible {
  outline: 2px solid var(--cnl-focus);
  outline-offset: 2px;
}

.cnl .cnl-demo__note {
  flex: 1 1 260px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-deep);
  margin: 0;
}

/* Shown only when the retries are spent. Warning ink rather than fail ink:
   nothing about the food is wrong, a request did not arrive, and the fail
   colour on a card full of HFSS verdicts would read as a compliance result.
   Colour is not carrying the meaning either way, the sentence is. */
.cnl .cnl-demo__stale {
  flex: 1 1 100%;
  font-size: 13px;
  line-height: 1.5;
  color: var(--status-warning-text);
  margin: 8px 0 0;
}

/* --------------------------------------------------------------------------
   Verified substrate
   -------------------------------------------------------------------------- */

.cnl .cnl-substrate {
  background: var(--bg-primary);
  padding: 96px var(--cnl-gut) 88px;
}

.cnl .cnl-substrate__inner {
  max-width: var(--cnl-max);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 520px 1fr;
  gap: 64px;
  align-items: start;
}

.cnl .cnl-substrate__headline {
  font-size: 42px;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--text-primary);
}

.cnl .cnl-substrate__body {
  font-size: 17px;
  line-height: 1.6;
  color: var(--text-deep);
  margin-top: 20px;
}

.cnl .cnl-substrate__facts {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
}

.cnl .cnl-fact {
  background: var(--bg-surface);
  border: 1px solid var(--cnl-hairline-dark);
  border-radius: var(--radius-card);
  padding: 22px 24px;
}

.cnl .cnl-fact__title { font-size: 20px; line-height: 1.2; letter-spacing: -0.01em; }

.cnl .cnl-fact__body {
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-deep);
  margin-top: 8px;
}

/* --------------------------------------------------------------------------
   Nutritionist seal
   --------------------------------------------------------------------------
   Live SVG, so the lettering is real text and the colours are tokens.

   MINT PLATE, DAMSON INK. The seal was series/sky carrying semantic/info-text,
   and the old comment here called that pairing "the only one legible on this
   plate". Measured, it is 4.23:1.

   THAT PASSED ON DESKTOP AND FAILED ON THE PHONE, which is the part worth
   keeping. Every size in this SVG is a user unit scaled by the rendered
   width, so the ring lettering is 30 units at both sizes but 24.9px at the
   232px seal and 18.4px at the 172px one. WCAG counts bold text as large from
   18.666px. The desktop ring is large text needing 3:1 and cleared 4.23; the
   phone ring lands 0.27px under the boundary, becomes normal text needing
   4.5, and did not. One declaration, one contrast ratio, two verdicts,
   decided by a quarter of a pixel at a breakpoint.

   So checking a scaled SVG at one size proves nothing about the other. The
   figure that moved was the threshold, not the colour.

   Every pair in the seal now, against the mint plate:

     ring text  NUTRITIONIST / VERIFIED   10.74:1   needs 4.5
     count      2,707                     10.74:1   needs 3.0
     label      INGREDIENTS               10.74:1   needs 4.5
     tick                                 10.74:1   needs 3.0

   One value, because everything on the plate is now --text-primary, and it
   clears the strictest threshold at both seal sizes with room to spare. So
   mint was not a concession to be defended on contrast: it answers the
   question Adam asked, and it retires the breakpoint-dependent failure on the
   way past.

   THE TICK IS DAMSON, NOT CREAM. Cream on mint is 1.58:1 and would have
   vanished. It was already the weakest thing here at 2.0:1 on the sky plate,
   which passes only because the tick is decorative: the seal says VERIFIED in
   words, so nothing is carried by the mark alone. Decorative is a reason it
   was not a defect, never a reason to keep it faint.

   The plate reads 1.58:1 against the cream section, down from 2.0. Neither
   figure is a requirement, because 1.4.11 asks about graphics needed to
   understand the content and a badge's outline is not one; its meaning is in
   its lettering, which is at 10.74. The hue shift carries the shape.

   NOT --status-pass. The compliance green is reserved by the Verdict Rule for
   a regulatory pass, and a marketing badge reading VERIFIED in it would
   assert one. That is the whole reason the mint rung exists.
   -------------------------------------------------------------------------- */

/* Centred in its own column. On desktop that column is the argument beside
   the facts, so the seal sits under the paragraph it belongs to; once the
   grid collapses the column is the full width and the same rule centres it
   on screen, with nothing breakpoint-specific to keep in step. */
.cnl .cnl-seal {
  display: block;
  width: 232px;
  height: 232px;
  margin: 36px auto 0;
}

.cnl .cnl-seal__plate { fill: var(--mint-plate); }

/* Every size in the seal is an SVG user unit inside a 280-unit viewBox, not a
   page pixel, so the exact value is free within a percent or two. They still
   take steps that are already on the ramp: it costs nothing visually and saves
   arguing the special case every time the detector runs.

   30/0.06em was measured off the comp, where the lettering occupies r
   0.71-0.87 of the plate. The previous 26/0.14em was both smaller and more
   loosely tracked, which pushed the words out toward the plus marks while
   leaving them visibly short of the comp's weight. */
.cnl .cnl-seal__ring {
  font-family: var(--font-primary);
  font-size: 30px;
  font-weight: 700;
  letter-spacing: 0.06em;
  fill: var(--text-primary);
}

.cnl .cnl-seal__plus {
  font-family: var(--font-primary);
  font-size: 23px;
  font-weight: 700;
  fill: var(--text-primary);
  text-anchor: middle;
}

/* The tick is a stroke, not a glyph, so it keeps its weight at any size.
   Damson rather than cream: see the tick note in the block comment above. */
.cnl .cnl-seal__tick {
  fill: none;
  stroke: var(--text-primary);
  stroke-width: 13;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.cnl .cnl-seal__count {
  font-family: var(--font-display);
  font-size: 42px;
  font-weight: 700;
  fill: var(--text-primary);
  text-anchor: middle;
}

.cnl .cnl-seal__label {
  font-family: var(--font-primary);
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.10em;
  fill: var(--text-primary);
  text-anchor: middle;
}

/* --------------------------------------------------------------------------
   Privacy and security
   -------------------------------------------------------------------------- */

.cnl .cnl-security {
  background: var(--text-primary);
  padding: 100px var(--cnl-gut);
}

.cnl .cnl-security__inner { max-width: var(--cnl-max); margin: 0 auto; }

.cnl-security__inner > .cnl-security__argument,
.cnl .cnl-security__inner > .cnl-security__refusals {
  display: block;
}

/* Argument left, refusals right, spec sheet beneath a hairline. */
.cnl .cnl-security__inner {
  display: grid;
  grid-template-columns: 520px 1fr;
  grid-template-areas:
    "argument refusals"
    "specs    specs";
  gap: 64px;
  align-items: start;
}

.cnl .cnl-security__argument { grid-area: argument; }
.cnl .cnl-security__refusals { grid-area: refusals; }
.cnl .cnl-specs { grid-area: specs; }

.cnl .cnl-security__headline {
  font-size: 46px;
  line-height: 1.04;
  letter-spacing: -0.02em;
  color: var(--text-on-dark);
  margin-top: 18px;
}

.cnl .cnl-security__body {
  font-size: 17px;
  line-height: 1.62;
  color: var(--text-on-dark-subtle);
  margin-top: 20px;
}

.cnl .cnl-security__link {
  display: inline-block;
  margin-top: 20px;
  font-size: 15px;
  font-weight: 600;
  color: var(--brand-gold);
  text-decoration: none;
}
.cnl .cnl-security__link:hover { text-decoration: underline; }

.cnl .cnl-refusal + .cnl-refusal {
  margin-top: 26px;
  padding-top: 26px;
  border-top: 1px solid var(--cnl-hairline);
}

.cnl .cnl-refusal__title {
  font-size: 22px;
  line-height: 1.2;
  letter-spacing: -0.008em;
  color: var(--mint-on-dark);
}

.cnl .cnl-refusal__body {
  font-size: 15px;
  line-height: 1.6;
  color: var(--text-on-dark-subtle);
  margin-top: 10px;
}

.cnl .cnl-specs {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 40px;
  /* No rule above this. A hairline the full width of the section drew a hard
     line across the page and cut the argument in two, when the specs are the
     evidence FOR the paragraph above them. Space separates them instead. */
  margin-top: 44px;
}

.cnl .cnl-spec__label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--brand-gold);
}

.cnl .cnl-spec__value {
  font-size: 14px;
  line-height: 1.58;
  color: var(--text-on-dark-subtle);
  margin-top: 12px;
}

/* The invitation: the one thing to DO in the middle of the page.
   ---------------------------------------------------------------------------
   It was a row bolted to the foot of the privacy section under a hairline.
   Its own full-width band says "different act" without needing a rule drawn
   above it, and the colour change alone does the separating.

   PUNCH, the same ground as the countdown near the top of the page. That band
   states the deadline; this one states the move. Bookending the argument in
   one colour makes the pair read as a single thread rather than two unrelated
   coloured stripes.

   A PHOTO BAND (Figma 915:211 / 982:435, 2026-09-08). The punch ground is
   gone; the band is 720 tall on desktop and 600 on phones so the picture
   reads, and the copy sits at the bottom on the blurred foreground. The ink
   wash is baked into the file, so nothing here overlays it: measured on the
   shipped jpg, the bottom 30% never rises above lum 85/255, which keeps the
   shell note above 5:1 and the gold button's boundary above 3:1 everywhere
   the copy can land. The section paints the ink token itself so the band
   is the right colour before the lazy image arrives. */

.cnl .cnl-invite {
  position: relative;
  background: var(--text-primary);
  min-height: 720px;
  display: flex;
  align-items: flex-end;
  padding: 56px var(--cnl-gut) 96px;
}

.cnl .cnl-invite__photo {
  position: absolute;
  inset: 0;
  margin: 0;
  overflow: hidden;
}
/* Same specificity as the global `.marketing-page img:not(...)` reset that
   forces height:auto; this sheet loads after it, so this wins. */
.marketing-page .cnl .cnl-invite__photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* CENTRED, AND THE LINE IS DISPLAY TYPE RATHER THAN A NOTE.
   The band used to run the button hard left with a 15px note filling the rest
   of the row, which read as a footnote with a button parked beside it. On a
   punch ground that is the loudest surface on the page and the quietest thing
   on it was the sentence. Adam, on the Figma treatment: "I prefer the figma
   one. It's stronger design."

   The pair is centred as a unit and the sentence is set at display size, so
   the band reads as one statement with its action attached rather than two
   things sharing a row. The note therefore hugs its text (flex 0 1 auto)
   instead of claiming the leftover width. */
.cnl .cnl-invite__inner {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: var(--cnl-max);
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  flex-wrap: wrap;
}

.cnl .cnl-invite__note {
  flex: 0 1 auto;
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--text-on-dark);
  margin: 0;
}

/* --------------------------------------------------------------------------
   The method (Figma 1210:9 / 1210:32, 2026-09-08)
   --------------------------------------------------------------------------
   A light band between the dark privacy section and the photo band, so the
   page breathes and the argument lands on its own ground. Three open
   statements under a hairline, deliberately no cards: boxed, they read as
   filler beside the page's other tiles. Muted ink on cream measures 5.9:1,
   and the primary ink 15.6:1. */
.cnl .cnl-method {
  background: var(--cream);
  padding: 100px var(--cnl-gut) 104px;
}
.cnl .cnl-method__inner { max-width: var(--cnl-max); margin: 0 auto; }
.cnl .cnl-method__head { max-width: 720px; }
.cnl .cnl-method__headline {
  font-size: 46px;
  line-height: 1.04;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  margin-top: 24px;
}
.cnl .cnl-method__body {
  font-size: 17px;
  line-height: 1.6;
  color: var(--text-muted);
  margin: 24px 0 0;
  max-width: 700px;
}
.cnl .cnl-method .cnl-btn { margin-top: 24px; }
.cnl .cnl-method__points {
  list-style: none;
  margin: 48px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 40px;
}
.cnl .cnl-method__point {
  border-top: 1px solid var(--border-divider);
  padding-top: 22px;
}
.cnl .cnl-method__label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0;
}
.cnl .cnl-method__title {
  font-size: 24px;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.008em;
  color: var(--text-primary);
  margin: 12px 0 0;
}
.cnl .cnl-method__text {
  font-size: 15px;
  line-height: 1.6;
  color: var(--text-muted);
  margin: 12px 0 0;
}
/* The demo's own pointer at the method: gold on ink, underlined because it
   sits inside running copy where colour alone would not mark a link. */
.cnl .cnl-demo__method {
  display: inline-block;
  margin-top: 12px;
  font-size: 15px;
  font-weight: 600;
  color: var(--brand-gold);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.cnl .cnl-demo__method:hover { color: var(--brand-gold-hover); }

/* --------------------------------------------------------------------------
   Social proof
   -------------------------------------------------------------------------- */

.cnl .cnl-proof {
  /* Butter, and it stays butter. This branch moved it to shell on 2026-09-02
     so the offer band below could be the loudest thing on the page. That
     reasoning died with the offer band: since 2026-09-08 the offer is a
     full-bleed kitchen photo, not a colour. Adam then chose butter here
     explicitly on 2026-09-11 ("I actually think that the butter background
     might actually be better") and had the Figma frames changed to match, so
     butter is now the canonical value on both frames. */
  background: var(--brand-gold);
  padding: 88px var(--cnl-gut) 96px;
}

.cnl .cnl-proof__inner { max-width: var(--cnl-max); margin: 0 auto; }

.cnl .cnl-proof__headline {
  font-size: 42px;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--text-primary);
}

.cnl .cnl-proof__lede {
  font-size: 17px;
  line-height: 1.5;
  color: var(--text-primary);
  margin-top: 12px;
}

.cnl .cnl-logos {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 14px;
  margin-top: 34px;
}

.cnl .cnl-logo {
  background: var(--cream-deep);
  border-radius: var(--radius-card-sm);
  /* 90, not 72. The plate's height is what caps the mark inside it, and a
     72px plate could only ever give a square logo 44px of ink. See the note
     on optical area below. */
  height: 90px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 14px 20px;
}

/* LOGOS ARE MATCHED ON OPTICAL AREA, NOT ON HEIGHT.
   ---------------------------------------------------------------------------
   Every mark used to be capped at max-height 44px, which sounds even and is
   not. These five run from 1.04:1 to 4.89:1, so an equal height gave them
   rendered widths from 46px to 211px and INK AREAS from 2,013 to 9,467 square
   pixels: a 4.7x spread. Nali read as a banner and The Garden of Eva, at
   46x44 inside a 251px plate, read as a smudge.

   That matters more than tidiness. These five logos are the only third-party
   proof on the page, so half of it was illegible, and the customers who look
   smallest are the ones with round marks rather than the ones with least to
   say.

   The eye weighs how much ink a mark puts on the page, which is roughly
   width times height, so the fix is to equalise area and let height fall out
   of each aspect ratio. The heights below are computed from the measured
   ratios against a 62px ceiling, which is what a 90px plate allows.

   Deliberately NOT perfectly equal: pure equal-area drops Nali to 31px and it
   then reads as shrunken beside a 68px square. These land at a 1.59x spread,
   down from 4.70x, which is close enough that no logo looks like a mistake
   and far enough that a wordmark still reads as a wordmark.

   max-height rather than height, because marketing.css forces height:auto on
   every image at a specificity this cannot beat cleanly; max-height sits
   alongside that rather than fighting it. */
.cnl .cnl-logo img {
  max-height: 44px;
  max-width: 100%;
  width: auto;
  object-fit: contain;
}

/* Each carries the `img` type selector deliberately. The base rule above is
   `.cnl .cnl-logo img`, which is (0,2,1); a class-only rule here would be
   (0,2,0) and LOSE to it, so every logo would silently stay at 44px and this
   whole block would look applied while doing nothing. Written without it
   first, and that is exactly what happened. */
/* Widest wordmark, 4.89:1. Renders 176 x 36. */
.cnl .cnl-logo img.cnl-logo__img--wide     { max-height: 36px; }
/* 3.86:1. Renders 154 x 40. */
.cnl .cnl-logo img.cnl-logo__img--wordmark { max-height: 40px; }
/* 2.71:1. Renders 125 x 46. */
.cnl .cnl-logo img.cnl-logo__img--regular  { max-height: 46px; }
/* 1.79:1, a stacked lockup. Renders 97 x 54. */
.cnl .cnl-logo img.cnl-logo__img--stacked  { max-height: 54px; }
/* 1.04:1, effectively square, and the one that was unreadable.
   Renders 64 x 62 against the old 46 x 44. */
.cnl .cnl-logo img.cnl-logo__img--square   { max-height: 62px; }

/* The Wildly Tasty mark carries a long descender on the 'y' and an underline
   under TASTY, so its bounding box runs well below its letters. Centring the
   box therefore hangs the wordmark high next to marks that have no descender.
   Adam's rule for this row is that the crossbar of the A in TASTY sits on the
   shared centre line.

   Measured, not guessed: rasterised at 8x, the crossbar is a clear ink spike
   at viewBox y 25.4 of 72. At the rendered 44px height that puts it 6.5px
   above the row centre. Expressed against the image's own height so it holds
   at the smaller mobile size rather than needing a second hardcoded nudge. */
.cnl .cnl-logo__img--descender {
  transform: translateY(14.7%);
}

/* .cnl-placeholder is gone on purpose. It was a loud mustard pill reading
   "no testimonials exist yet, do not ship", meant to stop lorem shipping.
   It put the warning on the page rather than in the build, so the only
   person it warned was the visitor, and what it told them was that no
   customer has praised us yet. The guard now lives in
   canoli-home-render-test, where it can fail a release instead.

   The quote styles below are kept though the markup is currently absent.
   Real quotes are coming, and a live rule is cheaper than reconstructing
   one from git. */

.cnl .cnl-quotes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  margin-top: 26px;
}

.cnl .cnl-quote {
  background: var(--brand-clay);
  border-radius: var(--radius-card);
  padding: 26px;
  margin: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 26px;
}

.cnl .cnl-quote--light {
  background: var(--bg-surface);
  border: 1px dashed var(--brand-gold-hover);
}

.cnl .cnl-quote__text {
  font-size: 18px;
  line-height: 1.5;
  font-weight: 600;
  color: var(--text-on-dark);
}
.cnl .cnl-quote--light .cnl-quote__text { color: var(--text-primary); }

.cnl .cnl-quote__by { display: flex; align-items: center; gap: 12px; }

.cnl .cnl-quote__avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--cream-deep);
  flex: 0 0 40px;
}

.cnl-quote__name,
.cnl .cnl-quote__role { display: block; }

.cnl .cnl-quote__name { font-size: 14px; font-weight: 700; color: var(--text-on-dark); }
.cnl .cnl-quote__role { font-size: 13px; color: var(--text-on-dark); }
.cnl-quote--light .cnl-quote__name,
.cnl .cnl-quote--light .cnl-quote__role { color: var(--text-primary); }

/* --------------------------------------------------------------------------
   Pricing
   -------------------------------------------------------------------------- */

.cnl .cnl-pricing {
  background: var(--bg-surface);
  padding: 96px var(--cnl-gut);
}

.cnl .cnl-pricing__inner { max-width: var(--cnl-max); margin: 0 auto; }

.cnl .cnl-pricing__headline {
  font-size: 42px;
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.cnl .cnl-pricing__lede {
  font-size: 17px;
  line-height: 1.5;
  color: var(--text-deep);
  margin-top: 12px;
}

.cnl .cnl-cadence {
  display: inline-flex;
  gap: 4px;
  margin-top: 28px;
  padding: 4px;
  background: var(--bg-muted);
  border-radius: var(--radius-toggle);
}

.cnl .cnl-cadence__btn {
  padding: 9px 20px;
  border: 0;
  border-radius: var(--radius-toggle);
  background: transparent;
  font-family: var(--font-primary);
  font-size: 14px;
  font-weight: 600;
  color: var(--text-deep);
  cursor: pointer;
}

.cnl .cnl-cadence__btn--on { background: var(--bg-surface); color: var(--text-primary); }

.cnl .cnl-tiers {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 18px;
  margin-top: 34px;
  align-items: stretch;
}

/* Cards stretch to a shared height and the CTA is pushed to the foot, so
   every button lands on the same line whatever the feature list does. */
.cnl .cnl-tier {
  display: flex;
  flex-direction: column;
  background: var(--bg-surface);
  border: 1px solid var(--cnl-hairline-dark);
  border-radius: var(--radius-card);
  padding: 28px;
  /* The badge is positioned against this. Not overflow:hidden, because the
     badge deliberately straddles the top edge. */
  position: relative;
}

/* The extra border pixel is taken back out of the padding, or the featured
   card's contents start 1px lower than the other three and the four names sit
   on two baselines instead of one. Invisible on its own; the point is that
   moving the badge out of flow only aligns the row if nothing else offsets
   it, and a 2px border against a 1px one is exactly that. */
.cnl .cnl-tier--featured {
  border-color: var(--brand-gold);
  border-width: 2px;
  padding: 27px;
}

/* OUT OF FLOW, ON THE CARD'S TOP EDGE.

   It used to be the first flex child, so it occupied a row inside the card
   and pushed everything under it down. Only Growth has a badge, so only
   Growth's contents moved: its name sat 25px below the other three and its
   price sat 24px below theirs. On a surface whose entire job is comparison,
   the four numbers a visitor is comparing did not share a baseline, and the
   one card carrying the badge was the one knocked out of line.

   Straddling the border rather than sitting under it. translateY(-50%) puts
   the badge's midline on the card's top edge, so it reads as a tab attached
   to the card rather than a chip floating above it, and the 2px gold border
   the featured card already carries runs behind it.

   No reserved row on the other three: nothing is in the flow to reserve for
   any more. */
.cnl .cnl-tier__badge {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 5px 12px;
  border-radius: var(--radius-chip);
  background: var(--brand-gold);
  color: var(--text-primary);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.cnl .cnl-tier__name { font-size: 22px; letter-spacing: -0.01em; }

/* TWO LINES RESERVED, whatever the copy does.

   This block sets how far down the price sits, so its height is what decides
   whether the four prices share a baseline. They did not: one line on Growth,
   two on Starter and Scale, three on Enterprise put the numbers a visitor is
   comparing at four different heights on a surface built for comparing them.

   The copy is now written to land on two lines at the widths this grid
   produces (see migration 20260901160000), and this is the belt to that
   braces. A column width is not a promise: a new breakpoint, a longer plan
   name or different font metrics can reflow any of these to one line or
   three. Copy sized to fit is what makes the common case read well, and the
   reserved height is what stops the row breaking when it does not.

   min-height rather than a fixed height, so a third line overflows the card
   gracefully instead of being clipped. If that ever happens the fix is the
   copy, and an unaligned row is a much better way to find out than a
   half-visible sentence. */
.cnl .cnl-tier__for {
  font-size: 13px;
  line-height: 1.45;
  min-height: calc(13px * 1.45 * 2);
  color: var(--text-deep);
  margin-top: 6px;
}

.cnl .cnl-tier__price {
  display: flex;
  align-items: baseline;
  gap: 3px;
  margin-top: 18px;
}

.cnl .cnl-tier__currency { font-family: var(--font-display); font-size: 26px; font-weight: 700; }
.cnl .cnl-tier__value { font-family: var(--font-display); font-size: 38px; font-weight: 700; line-height: 1.1; }
.cnl .cnl-tier__per { font-size: 14px; color: var(--text-deep); }

.cnl .cnl-tier__cadence {
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-label);
  margin-top: 8px;
}

.cnl .cnl-tier__rule {
  border: 0;
  border-top: 1px solid var(--border-primary);
  margin: 18px 0 0;
}

.cnl .cnl-tier__lead {
  font-size: 13px;
  color: var(--text-deep);
  margin-top: 18px;
}

.cnl .cnl-tier__features {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.cnl .cnl-tier__features li {
  font-size: 13px;
  line-height: 1.4;
  color: var(--text-deep);
}

/* margin-top:auto is what lands every card's button on one line. */
/* margin-top:auto is the ONLY thing landing every card's button on one
   line. A fixed margin here silently defeats it, which is exactly what a
   `features + cta` rule did on the first pass: the cards stretch to a
   shared height, so the button must be pushed to the foot rather than
   spaced from whatever happens to precede it. Minimum gap comes from the
   padding, not from a margin. */
.cnl .cnl-tier__cta {
  margin-top: auto;
  padding-top: 15px;
  padding-bottom: 15px;
}
.cnl .cnl-tier__features { margin-bottom: 28px; }

/* The what-happens-next line under the pricing lede. Sits between the lede
   and the cadence toggle, because it answers the question a buyer has BEFORE
   they compare prices, not after.

   Margin is 0 on the sides, NOT auto. The section is left-aligned: headline,
   lede and cadence toggle all start at the same left edge. An `auto` here
   centred this one block inside a 68ch measure, so it began roughly a third
   of the way across the section under a full-bleed headline, reading as a
   stray pull-quote rather than as the sentence that continues the lede. */
.cnl .cnl-pricing__terms {
  font-size: 15px;
  line-height: 1.6;
  color: var(--text-deep);
  max-width: 68ch;
  margin: 14px 0 0;
}

/* --------------------------------------------------------------------------
   Compare all features
   -------------------------------------------------------------------------- */

/* Visible to a screen reader, to nothing else. There was no such class in
   this codebase, which is part of why the comparison cells were going to be
   bare glyphs: a table of "tick cross tick cross" read aloud is worse than no
   table. clip-path plus a 1px box is the version that survives being inside a
   flex or grid child; display:none and visibility:hidden are both read as
   absent, and a negative text-indent breaks in RTL. */
.cnl .cnl-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* NO RULE ABOVE THIS. The border-top was drawing a hairline straight across
   the section under four cards that already have their own outlines, so the
   row read as sitting in a box someone had closed off. The cards are the
   grouping; a line under them adds a second, weaker one that disagrees with
   it.

   Space does the separating instead, which is the same move the rest of this
   page makes. 44px rather than the old 32 plus 20 of padding, so the gap is
   slightly larger than the line-plus-padding it replaces: a divider lets a
   small gap read as deliberate, and without one the same distance reads as
   the disclosure having drifted up into the cards. */
.cnl .cnl-compare {
  margin-top: 44px;
}

/* The disclosure's own control. list-style:none on both the element and the
   ::-webkit-details-marker is what removes the default triangle across
   engines; the caret is drawn below so it can rotate. */
.cnl .cnl-compare__summary {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  list-style: none;
  font-family: var(--font-primary);
  font-size: 15px;
  font-weight: 600;
  color: var(--text-primary);
  /* 24px of target height at a 15px label, per WCAG 2.5.8. */
  padding: 6px 2px;
}
.cnl .cnl-compare__summary::-webkit-details-marker { display: none; }

.cnl .cnl-compare__summary::after {
  content: "";
  width: 7px;
  height: 7px;
  border-right: 2px solid var(--text-deep);
  border-bottom: 2px solid var(--text-deep);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 0.18s ease;
}
.cnl .cnl-compare[open] .cnl-compare__summary::after {
  transform: rotate(-135deg) translate(-2px, -2px);
}

.cnl .cnl-compare__baseline {
  font-size: 15px;
  line-height: 1.6;
  color: var(--text-deep);
  margin: 14px 0 18px;
  max-width: 72ch;
}

/* The table is wider than a phone and must scroll INSIDE its own box rather
   than making the page scroll sideways.

   min-width:0 is the load-bearing line, not overflow-x. A block in a
   constrained context still resolves its min-width from its content, so the
   520px table pushed this box to 520px, the box then had nothing to scroll,
   and the PAGE scrolled instead: measured at 390 the document went from
   scrollWidth 390 with the disclosure closed to 460 with it open, and the
   viewport really did drag sideways by 45px.

   overflow-x:auto without min-width:0 is the single most common way a
   scrollable table is written and does not work. */
.cnl .cnl-compare__scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  min-width: 0;
  max-width: 100%;
  /* contain:paint is what actually fixes it, and min-width:0 alone does not.
     The box was already the right width (350px at a 390 viewport) and the
     table was already scrolling inside it, yet the DOCUMENT still reported
     scrollWidth 460 and really did drag sideways by 46px: a clipped
     descendant was still contributing to the ancestor's scrollable area,
     which body{overflow-x:hidden} did not stop.

     Established by measuring, not by guessing. At a 390 viewport, with the
     disclosure open: min-width:0 alone left it at 460; an explicit
     width:100% left it at 460; zeroing the table's own min-width got 395 and
     squashed the table; contain:paint got 390 with the table intact.

     It tells the browser this box's descendants never paint outside it, so
     their overflow stops here. */
  contain: paint;
  /* An edge fade so it is visible that there is more to the right. Without a
     cue the table's right edge sits flush with the container and reads as
     finished, which at 390 meant Scale and Enterprise, the two tiers worth
     most, appeared not to exist. mask-image rather than an overlay because an
     overlay would need a background colour and this sits on two different
     grounds across breakpoints. */
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%); /* hex-ok: mask channel, not a colour. In mask-image #000 means fully opaque and transparent means fully cut; a brand token here would partially mask the table instead of showing it. */
  mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent 100%); /* hex-ok: mask channel, not a colour. In mask-image #000 means fully opaque and transparent means fully cut; a brand token here would partially mask the table instead of showing it. */
}

/* The fade is a lie once you have scrolled to the end, so it only applies
   while there is something to scroll to. animation-timeline is progressive:
   where it is unsupported the fade simply stays, which is the safe failure. */
@supports (animation-timeline: scroll(self inline)) {
  .cnl .cnl-compare__scroll {
    animation: cnl-fade-end linear both;
    animation-timeline: scroll(self inline);
    animation-range: contain;
  }
  @keyframes cnl-fade-end {
    to {
      -webkit-mask-image: linear-gradient(to right, #000 100%, #000 100%); /* hex-ok: mask channel, not a colour. In mask-image #000 means fully opaque and transparent means fully cut; a brand token here would partially mask the table instead of showing it. */
      mask-image: linear-gradient(to right, #000 100%, #000 100%); /* hex-ok: mask channel, not a colour. In mask-image #000 means fully opaque and transparent means fully cut; a brand token here would partially mask the table instead of showing it. */
    }
  }
}

/* Same reason, one level up: the details element is the scroll box's parent
   and must not size itself to the table either. */
.cnl .cnl-compare { min-width: 0; }

.cnl .cnl-compare__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
  min-width: 520px;
}

/* The caption names the table for a screen reader that lists tables on a
   page. Hidden visually because the summary above already says it. */
.cnl .cnl-compare__caption {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/* The feature name column stays put while the tiers scroll past it.
   Scrolling right to reach Scale otherwise took the feature names off screen,
   so a reader arrived at a column of ticks with nothing to say what they were
   ticks FOR. The header row sticks for the same reason in the other axis. */
.cnl .cnl-compare__table tbody th {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--bg-surface);
}

.cnl .cnl-compare__table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bg-surface);
}

/* The corner cell overlaps both, so it needs to sit above each of them. */
.cnl .cnl-compare__table thead th:first-child { z-index: 3; }

.cnl .cnl-compare__table th,
.cnl .cnl-compare__table td {
  padding: 11px 14px;
  border-bottom: 1px solid var(--border-divider);
  text-align: left;
}

.cnl .cnl-compare__table thead th {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-deep);
  border-bottom-width: 2px;
}

.cnl .cnl-compare__table tbody th {
  font-weight: 500;
  color: var(--text-deep);
}

.cnl .cnl-compare__cell { text-align: center; }

/* Pass green and a muted dash, not green and red. A feature your tier does
   not include is not an error, and painting it red reads as one. */
/* The seal's tick, drawn rather than set. A stroke keeps its weight at any
   size, which is the same reason the seal draws it: at 17px a &check; glyph
   rendered noticeably lighter than the mark on the seal a few sections above,
   so the two read as different symbols doing the same job.

   Sized in em so it tracks the row's type, and vertical-align rather than a
   flex parent because these cells are table cells with text beside them. */
.cnl .cnl-compare__yes {
  width: 1.05em;
  height: 1.05em;
  vertical-align: -0.15em;
  fill: none;
  /* --text-primary, NOT --status-pass-text, and this is the Verdict Rule
     rather than a preference.

     tokens-canoli.css says it twice in prose: the compliance green is
     reserved for a regulatory pass, and green stays scoped to one. This table
     paints fourteen ticks, and until now it painted them in #135C33, which is
     the exact colour the demo card two thousand pixels above uses for
     "HFSS - NPM 2004/05 PASS". Measured on the live page after swapping the
     honey: the verdict text and the tick stroke were both rgb(19, 92, 51). So
     one colour meant "your product passes HFSS" and "this plan includes cost
     modelling" on the same page.

     The glyph swap I made an hour ago made it worse rather than causing it.
     The colour was already on the &check;, but a light system glyph and a
     stroked mark do not read as the same symbol; giving the tick the seal's
     own path made the two identical in shape as well as hue.

     The seal went to the trouble of inventing --mint-plate precisely so a
     marketing badge could not borrow this authority. A pricing table asserting
     fourteen compliance passes is the same mistake with more instances. The
     table's own ink says "yes" perfectly well and asserts nothing. */
  stroke: var(--text-primary);
  stroke-width: 16;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.cnl .cnl-compare__no {
  color: var(--decorative-muted);
  font-size: 17px;
}

/* Gated on, not built. It must not look like a tick and must not look like a
   dash: it is a promise with a date attached rather than a feature you can
   use on Monday, and the page is worth less if a buyer finds that out after
   paying. Butter tint carries the same "in progress" meaning the app uses. */
.cnl .cnl-compare__soon {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--radius-chip);
  background: var(--status-warning-bg);
  color: var(--status-warning-text);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* --------------------------------------------------------------------------
   Coming next: the app
   --------------------------------------------------------------------------

   Damson, between the butter proof band and the white pricing block. That
   also suits the artwork: two of the three screens are Bench Mode's dark
   espresso and would vanish on cream, while the light one reads as a
   deliberate contrast rather than an odd one out. */

.cnl .cnl-app {
  background: var(--text-primary);
  padding: 96px var(--cnl-gut);
}

/* TWO EQUAL COLUMNS: the argument on the left, one screen centred on the
   right. Three phones in a row made this the tallest section on the page
   while the copy column sat half empty above them.

   EQUAL, and the carousel CENTRED in its half, which is the whole point.
   `auto` on the second column sized it to the phone's 326px and parked it
   hard against the right edge of the 1140px inner, which opened a 280px void
   straight down the middle of the section between the end of the paragraphs
   and the start of the phone. Nothing lived there and nothing could.

   Equal columns also make the copy column narrower, so the paragraphs run to
   more lines and the two sides come closer in height. That is a second fix
   for free: the vertical gaps above and below the copy are what is left over
   when a short column is centred against a tall one, and they shrink as the
   copy grows. */
.cnl .cnl-app__inner {
  max-width: var(--cnl-max);
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 40px 64px;
  align-items: center;
}

.cnl .cnl-app__copy { max-width: 620px; }

.cnl .cnl-app__headline {
  font-size: 42px;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--text-on-dark);
  margin-top: 14px;
}

.cnl .cnl-app__body {
  font-size: 17px;
  line-height: 1.62;
  color: var(--text-on-dark-subtle);
  margin-top: 18px;
}

/* Paragraph to paragraph, rather than headline to paragraph. 18px is under a
   single 27.5px line here, so two paragraphs at that gap read as one block
   that happens to have a short line in the middle. The second paragraph
   answers the first, so it needs to look like a separate thought. */
.cnl .cnl-app__body + .cnl-app__body { margin-top: 22px; }

/* --------------------------------------------------------------------------
   The screen carousel
   -------------------------------------------------------------------------- */

/* Centred in its own half rather than sitting at the far edge of the row.

   280 rather than the artwork's natural 326. The two columns are centred
   against each other, so whatever height the carousel does not share with the
   copy is split into equal gaps above and below the copy, and those gaps were
   the two voids left after the horizontal one was closed. Every pixel off the
   phone's height takes half a pixel off each of them.

   The mobile rule keeps 300 rather than following this down, and that is not
   an oversight. Below the breakpoint the section is stacked, so there is no
   second column to be short against and no void for a smaller phone to close.
   The only thing shrinking it would do there is make the screen detail harder
   to read on the device where it is already hardest. The two numbers differ
   because the two layouts are answering different questions. */
.cnl .cnl-app__carousel {
  width: 280px;
  justify-self: center;
}

/* Every slide in ONE grid cell. That is what stops the dots moving as the
   slide changes: the stage is as tall as the tallest slide from the start,
   rather than resizing to whichever caption is showing. */
.cnl .cnl-app__stage { display: grid; }

.cnl .cnl-app__slide {
  grid-area: 1 / 1;
  opacity: 0;
  visibility: hidden;
  transition: opacity 420ms ease;
}

.cnl .cnl-app__slide.is-active {
  opacity: 1;
  visibility: visible;
}

/* visibility, NOT display or the hidden attribute. A hidden-by-visibility
   element is still laid out, so its lazy image loads when the stage scrolls
   into view; the other two do not, and the first auto-advance would show an
   empty box while the image fetched. It is also enough to take the inactive
   slides out of the accessibility tree, so they need no aria-hidden. */

.cnl .cnl-app__slide img {
  display: block;
  width: 100%;
  height: auto;
  /* Matches the artwork, which is a 393x846 frame with a 40px device radius.
     A percentage radius is horizontal-% of width over vertical-% of height, so
     40/393 over 40/846 resolves to the SAME 40 design units at every rendered
     width while the aspect ratio holds, which it does here (width:100% and
     height:auto). A fixed px value cannot: --radius-card is 18px, and against
     the artwork's 33px arc at this size it left a visible wedge of the export
     background showing at each corner.

     The export is what put a colour in that wedge. These were previously
     rendered WITH Figma's canvas behind them, baking opaque #F5F5F5 into the
     corners; a light grey blob on a damson ground is what "sloppy corners"
     looked like. Re-exported through download_assets, the corners carry alpha
     0, so even a mismatch would now show damson rather than grey. Both halves
     are fixed rather than just the visible one, because either alone leaves
     the hairline below tracing a rectangle around a rounded image. */
  border-radius: 10.178% / 4.728%;
  /* The screens are phone frames on a dark ground, so without an edge they
     bleed into the section. A hairline rather than a shadow: a drop shadow on
     damson reads as smudge rather than lift. */
  border: 1px solid var(--cnl-hairline);
}

/* Centred, because the dots below it are. Left-aligned under a centred row
   of controls the caption read as belonging to something else. It is also
   balanced, so the two lines are even rather than a long line over a stub. */
.cnl .cnl-app__caption {
  font-size: 15px;
  line-height: 1.55;
  color: var(--text-on-dark-subtle);
  margin-top: 14px;
  text-align: center;
  text-wrap: balance;
}

.cnl .cnl-app__dots {
  display: flex;
  justify-content: center;
  gap: 10px;
  /* 8px, not 20. The button is 24px tall around an 8px dot, so it carries its
     own 8px of clearance above the dot already; 20 on top of that read as a
     detached row rather than as this carousel's controls. */
  margin-top: 8px;
}

/* The dot is 8px, the BUTTON is 24px. WCAG 2.5.8 asks for a 24x24 target and
   three dots at 8px with a 10px gap would be a row of targets you have to aim
   at. The padding is the target; the dot is only what you can see of it. */
.cnl .cnl-app__dot {
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  display: grid;
  place-items: center;
}

.cnl .cnl-app__dot::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  /* Not the hairline colour: an unselected dot still has to be findable, and
     a control you cannot see is not a control. This clears 3:1 on damson,
     which is what 1.4.11 asks of a control boundary. */
  background: var(--text-on-dark-muted);
  transition: background 200ms ease, transform 200ms ease;
}

.cnl .cnl-app__dot.is-on::before {
  background: var(--brand-gold);
  transform: scale(1.25);
}

.cnl .cnl-app__dot:hover::before { background: var(--text-on-dark-subtle); }
.cnl .cnl-app__dot.is-on:hover::before { background: var(--brand-gold); }

.cnl .cnl-app__dot:focus-visible {
  outline: 2px solid var(--cnl-focus);
  outline-offset: 2px;
  border-radius: 50%;
}

/* Nothing here moves for anyone who has asked it not to. The component also
   never starts its timer under this query, so this is only about the fade and
   the dot growing; without it a reduced-motion user who chose a dot would
   still get a cross-fade they did not ask for. */
@media (prefers-reduced-motion: reduce) {
  .cnl .cnl-app__slide { transition: none; }
  .cnl .cnl-app__dot::before { transition: none; }
  .cnl .cnl-app__dot.is-on::before { transform: none; }
}

/* The status line, last in the copy column. Quiet, but not hidden: it is the
   sentence that stops the screens reading as something a visitor can download
   today. */
.cnl .cnl-app__note {
  font-size: 14px;
  line-height: 1.6;
  /* The SAME colour as the paragraphs above it. It used --text-on-dark-muted,
     which is rgba(255,255,255,0.8) and on this damson ground renders BRIGHTER
     than the --text-on-dark-subtle the body uses. So the one line that exists
     to hold the section back was the lightest text in it and took the eye
     first. "Muted" names the token's intent, not its weight against every
     ground, and this ground is the one it loses on. */
  color: var(--text-on-dark-subtle);
  /* Left, in the copy column, under the paragraphs it qualifies. It spent a
     revision centred across both columns at the foot, which cost a band of
     section height for one line. */
  margin-top: 20px;
}

/* --------------------------------------------------------------------------
   Newsletter
   -------------------------------------------------------------------------- */

.cnl .cnl-news {
  background: var(--bg-primary);
  padding: 80px var(--cnl-gut) 84px;
  text-align: center;
}

.cnl .cnl-news__inner { max-width: 640px; margin: 0 auto; }

/* The mark, taken from atom-mask.svg's alpha and painted with a token.

   -webkit-mask is still carried alongside the standard property: Safari only
   dropped the prefix requirement recently and this is a marketing page, so
   the cost of the extra line is nothing and the cost of missing it is a blank
   64px gap above the headline on older iOS.

   Damson on shell measures 16.1:1, though the ratio is decorative here: the
   glyph carries no information the section does not state in words, which is
   why it is aria-hidden in the markup. */
/* BIG, because it is the joke. "Stay in the loop" over the ring-and-point
   mark is a pun, and a pun only lands if you notice both halves at once. At
   64px this read as a small logo doing duty above a headline, which is a
   different thing entirely: the eye files it as chrome and moves on. At 132px
   it is the loop the sentence is talking about.

   It stays aria-hidden even though it now carries meaning, because the
   meaning is the VISUAL rhyme. The headline already says "loop" in words, and
   announcing a logo here would give a screen reader the setup with no
   punchline, which is worse than letting the sentence stand on its own. */
.cnl .cnl-news__mark {
  display: block;
  width: 132px;
  height: 132px;
  margin: 0 auto 28px;
  background: var(--text-primary);
  -webkit-mask: url("/img/canoli/atom-mask.svg") center / contain no-repeat;
  mask: url("/img/canoli/atom-mask.svg") center / contain no-repeat;
}

.cnl .cnl-news__headline { font-size: 30px; letter-spacing: -0.018em; line-height: 1.15; }

.cnl .cnl-news__body {
  font-size: 16px;
  line-height: 1.6;
  color: var(--text-deep);
  margin-top: 12px;
}

.cnl .cnl-news__form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 22px;
  align-items: center;
}

.cnl .cnl-news__label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Honeypot. Off-screen rather than display:none, which some bots skip. */
.cnl .cnl-news__hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.cnl .cnl-news__input {
  width: 100%;
  max-width: 380px;
  padding: 14px 20px;
  border: 1px solid var(--border-primary);
  border-radius: var(--radius-field);
  font-family: var(--font-primary);
  font-size: 15px;
  color: var(--text-primary);
  background: var(--bg-surface);
}
.cnl .cnl-news__input::placeholder { color: var(--text-label); }
.cnl .cnl-news__input:focus-visible { outline: 2px solid var(--cnl-focus); outline-offset: 2px; }

.cnl .cnl-news__btn { width: 100%; max-width: 380px; }

.cnl .cnl-news__error {
  font-size: 13px;
  color: var(--status-fail);
  min-height: 1em;
}

.cnl .cnl-news__thanks { display: none; max-width: 640px; margin: 0 auto; }

/* --------------------------------------------------------------------------
   Close
   -------------------------------------------------------------------------- */

.cnl .cnl-close {
  background: var(--brand-clay);
  padding: 104px var(--cnl-gut);
  text-align: center;
}

.cnl .cnl-close__inner { max-width: 820px; margin: 0 auto; }

.cnl .cnl-close__headline {
  font-size: 42px;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--text-on-dark);
}

.cnl .cnl-close__body {
  font-size: 17px;
  line-height: 1.6;
  color: var(--text-on-dark);
  margin-top: 16px;
}

.cnl .cnl-close .cnl-btn { margin-top: 26px; }

.cnl .cnl-close__note {
  font-size: 14px;
  color: var(--text-on-dark);
  margin-top: 16px;
}

/* The pager exists only where the cards scroll. Above the breakpoint the grid
   shows all four at once, so a dot row would be an indicator for a carousel
   that is not there. */
.cnl .cnl-tiers__dots { display: none; }

/* --------------------------------------------------------------------------
   Footer
   --------------------------------------------------------------------------
   Moved to marketing.css as .footer--columns. It has to render on /terms,
   /privacy, /about and /blog too, and none of those load this stylesheet or
   sit inside .cnl. Only the gutter override stays here, because this page
   uses its own wider gutter than the other marketing pages.
   -------------------------------------------------------------------------- */

.cnl .footer--columns { padding: 64px var(--cnl-gut); }

/* --------------------------------------------------------------------------
   Mobile
   --------------------------------------------------------------------------
   The comp's mobile board is 390px. This block ends at 700px rather than
   900px so the tablet range below can build on the DESKTOP rules instead.
   Stacking the two is what produced the iPad complaint: a phone layout
   stretched to 820px gives 780px-wide buttons and a 34px headline in a
   viewport with room for far more.

   Below 700px the page goes to a single
   column; the hero scrim also changes AXIS, because the copy stacks from
   the top here and a horizontal ramp would protect the wrong side.
   -------------------------------------------------------------------------- */

@media (max-width: 700px) {
  .cnl { --cnl-gut: 20px; }

  .cnl .cnl-nav { padding: 14px var(--cnl-gut); }
  .cnl .cnl-nav__links { display: none; }
  .cnl .cnl-burger { display: flex; }

  /* The burger becomes an X while the drawer is open.
     ---------------------------------------------------------------------
     It never did, so the only way to discover the drawer could be closed
     was to guess that the same three lines still toggled. Tapping it did
     work; nothing told anyone it would.

     The bars are 2px tall with a 6px gap, so each outer bar travels 8px to
     meet the middle one. The middle bar fades rather than moving, which is
     what stops the X reading as three lines stacked on top of each other. */
  .cnl .cnl-burger__bar { transition: transform 0.18s ease, opacity 0.18s ease; }
  .cnl .cnl-burger[aria-expanded="true"] .cnl-burger__bar:nth-child(1) {
    transform: translateY(8px) rotate(45deg);
  }
  .cnl .cnl-burger[aria-expanded="true"] .cnl-burger__bar:nth-child(2) {
    opacity: 0;
  }
  .cnl .cnl-burger[aria-expanded="true"] .cnl-burger__bar:nth-child(3) {
    transform: translateY(-8px) rotate(-45deg);
  }

  .cnl .cnl-nav__drawer {
    display: flex;
    flex-direction: column;
    gap: 4px;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    padding: 12px var(--cnl-gut) 20px;
    background: var(--text-primary);
    border-top: 1px solid var(--cnl-hairline);
    /* The drawer is the same damson as the hero it opens over, and it had
       z-index:auto and no edge below it. So it painted over the H1 with no
       visible boundary: the headline did not slide behind a panel, it simply
       vanished, and the hero lede sat 10px under an invisible edge. It read
       as the page having glitched rather than as a menu opening.

       A bottom edge and a shadow give it a boundary against an identical
       ground, and the z-index puts it above the hero's ring plane rather
       than relying on source order. */
    z-index: 50;
    border-bottom: 1px solid var(--cnl-hairline);
    box-shadow: 0 18px 32px color-mix(in srgb, var(--ink-deep) 45%, transparent);
  }
  .cnl .cnl-nav__drawer a {
    color: var(--text-on-dark);
    text-decoration: none;
    font-size: 16px;
    padding: 12px 0;
  }
  .cnl .cnl-nav__drawer .cnl-btn { margin-top: 8px; justify-content: center; }

  /* The drawer's link colour is (0,3,1) and the button's own colour is
     (0,2,0), so without this the primary CTA renders on-dark cream on a
     butter plate: invisible. Anchored on the anchor to outrank it. */
  .cnl .cnl-nav__drawer a.cnl-btn--primary { color: var(--text-primary); }
  .cnl .cnl-nav__drawer a.cnl-btn--dark { color: var(--text-on-dark); }
  .cnl [x-cloak] { display: none !important; }

  .cnl .cnl-hero {
    min-height: 0;
    padding: 64px 0 0;
    align-items: flex-start;
  }

  /* MOBILE NEEDED MORE THAN THE DESKTOP NUDGE, because the geometry is not
     the same problem wearing a different width.

     Stacked, the stations sit at 73% to 87% of the hero's height, and this
     gradient had already collapsed to 0.04 at 74% and nothing below. So the
     four stations sat in the clear, and it is the DENSEST part of the ring
     field: 68 of 101 ring nodes overlap that block, with their stroke widths
     summing to 335px across a band only 140px tall. The strokes cover it more
     than twice over, so this is not an occasional glyph clipping a line, it
     is type set on top of the drawing.

     Measured at the rings' peak, station text read 1.84:1 to 2.72:1. The
     labels are 23px and the numbers 15px, and 15px is below every large-text
     threshold, so those need 4.5:1 rather than 3:1. That is why the floor
     here is around 0.58 where the desktop radial only needed 0.34: a harder
     requirement over a busier background.

     It stays a bottom-anchored ramp rather than a panel, and the stops above
     57% are untouched, so nothing behind the headline or the ring centre
     changes. What changes is that the gradient no longer gives up right where
     the content it exists to protect begins. */
  .cnl .cnl-hero__scrim {
    background: linear-gradient(
      to bottom,
      color-mix(in srgb, var(--text-primary) 96%, transparent) 0%,
      color-mix(in srgb, var(--text-primary) 90%, transparent) 40%,
      color-mix(in srgb, var(--text-primary) 80%, transparent) 57%,
      color-mix(in srgb, var(--text-primary) 62%, transparent) 71%,
      color-mix(in srgb, var(--text-primary) 56%, transparent) 90%,
      color-mix(in srgb, var(--text-primary) 24%, transparent) 100%);
  }

  .cnl .cnl-hero__seam { height: 90px; }

  .cnl .cnl-hero__inner {
    flex-direction: column;
    align-items: stretch;
    gap: 66px;
    padding-bottom: 90px;
  }

  .cnl .cnl-hero__copy { flex: 1 1 auto; max-width: none; }
  .cnl .cnl-hero__headline { font-size: 34px; letter-spacing: -0.016em; line-height: 1.04; }
  .cnl .cnl-hero__lede { font-size: 16px; }
  .cnl .cnl-hero__ctas { flex-direction: column; }
  .cnl .cnl-hero__ctas .cnl-btn { width: 100%; }
  .cnl .cnl-hero__note { font-size: 13px; }

  .cnl .cnl-stations { gap: 13px; }
  .cnl .cnl-stations::before { inset: -110px -80px; }
  .cnl .cnl-station { gap: 11px; }
  .cnl .cnl-station__n { font-size: 15px; }
  .cnl .cnl-station__label { font-size: 23px; }

  /* Two by two, not four rows: a stacked list wastes the column and reads
     as a ledger rather than a set of figures. */
  .cnl .cnl-counters__grid { grid-template-columns: repeat(2, 1fr); gap: 22px 14px; }
  .cnl .cnl-counter__value { font-size: 26px; }

  .cnl .cnl-why-now { padding: 72px var(--cnl-gut); }
  .cnl .cnl-why-now__headline { font-size: 28px; }
  .cnl .cnl-why-now__body { font-size: 16px; }

  .cnl .cnl-bench { min-height: 0; display: block; padding: 60px var(--cnl-gut) 0; }
  .cnl .cnl-bench__inner { padding-right: 0; }
  .cnl .cnl-bench__copy { max-width: none; }
  /* Stacked: the photo drops under the copy as a full-width bleed. */
  .cnl .cnl-bench__photo {
    position: static;
    width: auto;
    height: 440px;
    margin: 24px calc(-1 * var(--cnl-gut)) 0;
  }
  .cnl .cnl-bench__headline { font-size: 30px; }
  .cnl .cnl-bench__body { font-size: 16px; max-width: none; }
  /* The turn keeps a step above the body here too. Sharing one rule with it
     was what made them the same size in the first place. */
  .cnl .cnl-bench__turn { font-size: 22px; max-width: none; }

  .cnl .cnl-substrate, .cnl .cnl-security { padding: 64px var(--cnl-gut); }
  .cnl .cnl-substrate__inner { grid-template-columns: 1fr; gap: 30px; }
  .cnl .cnl-substrate__headline { font-size: 30px; }

  .cnl .cnl-security__inner {
    grid-template-columns: 1fr;
    grid-template-areas: "argument" "refusals" "specs";
    gap: 34px;
  }
  .cnl .cnl-security__headline { font-size: 30px; }
  .cnl .cnl-specs { grid-template-columns: 1fr; gap: 26px; padding-top: 30px; }

  .cnl .cnl-method { padding: 60px var(--cnl-gut) 64px; }
  .cnl .cnl-method__headline { font-size: 28px; }
  .cnl .cnl-method__body { font-size: 16px; }
  .cnl .cnl-method .cnl-btn { width: 100%; }
  .cnl .cnl-method__points { grid-template-columns: 1fr; gap: 28px; margin-top: 32px; }
  .cnl .cnl-method__title { font-size: 22px; }

  /* 600 tall on the phone frame, copy 56px off the bottom. */
  .cnl .cnl-invite { min-height: 600px; padding: 40px var(--cnl-gut) 56px; }
  .cnl .cnl-invite__inner { gap: 16px; }
  /* The frame draws the phone button the full column width. */
  .cnl .cnl-invite .cnl-btn { width: 100%; }
  /* The display line steps down with the rest of the page, and centres its
     own text once it is the full column width rather than hugging. */
  .cnl .cnl-invite__note { font-size: 22px; text-align: center; }

  /* The two columns become one. The carousel was already one screen at a
     time, so nothing about it has to change for a phone: what was a sideways
     snap scroller here is now the same component the desktop uses, which is
     one behaviour to reason about instead of two. */
  .cnl .cnl-app { padding: 64px var(--cnl-gut); }
  .cnl .cnl-app__inner {
    grid-template-columns: none;
    gap: 36px;
  }
  .cnl .cnl-app__headline { font-size: 30px; }
  .cnl .cnl-app__body { font-size: 16px; }

  /* Never wider than the column it is in. The desktop width is a fixed 326px,
     which overflows a 390px screen once the section gutters are taken off. */
  .cnl .cnl-app__carousel {
    width: 100%;
    max-width: 300px;
    margin: 0 auto;
  }
  .cnl .cnl-app__caption { font-size: 14px; margin-top: 12px; }
  .cnl .cnl-app__note { font-size: 13px; margin-top: 16px; }

  .cnl .cnl-proof { padding: 64px var(--cnl-gut); }
  .cnl .cnl-proof__headline { font-size: 30px; }
  .cnl .cnl-logos { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  /* The same ratios at phone scale: a 72px plate with 12px padding gives a
     48px ceiling, and each mark keeps its share of it. */
  .cnl .cnl-logo { height: 72px; padding: 12px 16px; }

  /* Five logos in a 2-up grid leaves the fifth alone in the left column,
     which reads as a layout that ran out rather than one that ended. It
     spans both columns and centres at one column's width, so the last row
     is deliberately centred instead of orphaned.

     :nth-child(odd) is doing real work: with an even number of customers
     there is no orphan, and this rule must not fire and stretch a logo that
     already has a neighbour. Add a sixth and the grid simply goes back to
     three tidy rows with no edit here. */
  .cnl .cnl-logo:last-child:nth-child(odd) {
    grid-column: 1 / -1;
    width: 170px;
    justify-self: center;
  }
  .cnl .cnl-logo img { max-height: 34px; }
  .cnl .cnl-logo img.cnl-logo__img--wide     { max-height: 28px; }
  .cnl .cnl-logo img.cnl-logo__img--wordmark { max-height: 31px; }
  .cnl .cnl-logo img.cnl-logo__img--regular  { max-height: 36px; }
  .cnl .cnl-logo img.cnl-logo__img--stacked  { max-height: 42px; }
  .cnl .cnl-logo img.cnl-logo__img--square   { max-height: 48px; }

  /* A single row that scrolls, with the next card deliberately peeking so
     the gesture is discoverable. */
  .cnl .cnl-quotes {
    grid-template-columns: none;
    display: flex;
    gap: 12px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 4px;
  }
  .cnl .cnl-quote {
    flex: 0 0 296px;
    scroll-snap-align: start;
  }
  .cnl .cnl-quote__text { font-size: 17px; }

  .cnl .cnl-pricing { padding: 64px var(--cnl-gut); }
  .cnl .cnl-pricing__headline { font-size: 30px; }
  /* THE PLAN CARDS BECOME A SWIPE RAIL.
     -----------------------------------------------------------------------
     Adam, on the column this replaces: "if the most popular is the default
     and there is a sneak peak of a card either side to show the other plans
     exist. This feels better."

     The peek is what makes it work. A carousel's real cost is discovery,
     because a reader who does not swipe never learns the other plans are
     there, and on a pricing page that is the expensive kind of hidden. Two
     cards showing at the edges answer that before anyone has to guess.

     Comparing plans is still a job the column did better, and it has not
     been lost: the feature table below this is the comparison tool. The
     cards choose, the table compares.

     The rail bleeds through the section gutter (padding plus a negative
     margin) so a card can sit centred with its neighbours visibly running
     off both edges, rather than a scroller that stops at the text column. */
  .cnl .cnl-tiers {
    display: flex;
    grid-template-columns: none;
    gap: 12px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding: 8px var(--cnl-gut) 4px;
    margin-inline: calc(var(--cnl-gut) * -1);
    scrollbar-width: none;
  }
  .cnl .cnl-tiers::-webkit-scrollbar { display: none; }

  .cnl .cnl-tier {
    flex: 0 0 78%;
    scroll-snap-align: center;
  }

  /* Snap padding keeps the first and last card centrable: without it the
     rail runs out of scroll before Starter or Enterprise reach the middle,
     and the dots would point at a card the reader cannot centre. */
  .cnl .cnl-tiers::before,
  .cnl .cnl-tiers::after {
    content: "";
    flex: 0 0 11%;
  }

  .cnl .cnl-tiers__dots {
    display: flex;
    justify-content: center;
    gap: 6px;
    list-style: none;
    margin: 16px 0 0;
    padding: 0;
  }

  /* 24px of target around an 8px dot, which is 2.5.8's minimum. The dot is
     drawn by the pseudo-element so the button can be finger-sized without
     the mark growing with it. */
  .cnl .cnl-tiers__dot {
    display: grid;
    place-items: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
  }

  .cnl .cnl-tiers__dot::before {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--decorative-muted);
    opacity: 0.4;
    transition: opacity 0.16s ease, background-color 0.16s ease;
  }

  .cnl .cnl-tiers__dot[aria-current="true"]::before {
    background: var(--text-primary);
    opacity: 1;
  }
  .cnl .cnl-tier__cta { margin-top: 28px; }
  .cnl .cnl-tier__features + .cnl-tier__cta { margin-top: 28px; }

  .cnl .cnl-news { padding: 56px var(--cnl-gut) 60px; }
  .cnl .cnl-news__headline { font-size: 26px; }
  /* Scaled with the headline rather than held at 132. The pun needs the mark
     and the sentence to read as one thing, and a 132px loop over a 26px line
     on a 390px screen makes the mark the section and the sentence a caption
     under it. */
  .cnl .cnl-news__mark { width: 96px; height: 96px; margin-bottom: 22px; }
  .cnl .cnl-news__input, .cnl .cnl-news__btn { max-width: none; }

  .cnl .cnl-close { padding: 72px var(--cnl-gut); }
  .cnl .cnl-close__headline { font-size: 30px; }
  .cnl .cnl-close .cnl-btn { width: 100%; }

  .cnl .footer--columns {
    flex-direction: column;
    align-items: flex-start;
    gap: 32px;
    padding: 48px var(--cnl-gut);
  }

  /* Countdown: five units will not fit across 350px at desktop size, and
     shrinking them all until they do leaves a row of unreadable digits.
     Wrapping keeps every unit legible instead. */
  /* The timeline stacks: a 118px date column beside prose is fine at 1440 and
     leaves the description about 190px at 390, which breaks every row onto
     four lines. The date becomes its own line instead, and reads as a heading
     for the rule under it. */
  /* The chain is a sequence, so on one column it stacks and the chevron
     turns to point down. Rotating the same two borders keeps one shape doing
     the job at both widths. */
  .cnl .cnl-chain { grid-template-columns: 1fr; gap: 30px; }
  .cnl .cnl-chain__step { padding: 22px 20px; }
  .cnl .cnl-chain__step + .cnl-chain__step::before {
    left: 50%;
    top: -21px;
    transform: translateX(-50%) rotate(135deg);
  }

  /* Six tiles across a phone would be 50px each. Three across at two rows
     keeps the month legible, which is the only thing on the face. */
  .cnl .cnl-rules { margin-top: 40px; }
  .cnl .cnl-rules__tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
  .cnl .cnl-daytile__month { font-size: 30px; padding: 10px 0 4px; }
  .cnl .cnl-daytile__territory { padding-bottom: 12px; }
  .cnl .cnl-rule { padding: 20px 18px; }
  /* The tiles wrap to two rows here, and a pointer offset along one axis
     cannot say which row it means. It would confidently point at the wrong
     tile, which is worse than not pointing at all. */
  .cnl .cnl-rule::before { display: none; }
  .cnl .cnl-rule__title { font-size: 18px; }
  .cnl .cnl-rule__body { font-size: 15px; }

  .cnl .cnl-demo { padding: 56px var(--cnl-gut) 64px; }
  .cnl .cnl-demo__inner { grid-template-columns: 1fr; gap: 28px; }
  .cnl .cnl-demo__headline { font-size: 30px; }
  .cnl .cnl-demo__lede { font-size: 16px; }
  .cnl .cnl-demo__card { padding: 20px 18px 18px; margin-top: 28px; }
  .cnl .cnl-demo__head { flex-direction: column; align-items: flex-start; gap: 2px; }
  /* Five tiles cannot hold a mono value across 350px, so two up. */
  .cnl .cnl-metrics { grid-template-columns: repeat(2, 1fr); }

  /* THE VERDICTS FOLLOW YOU DOWN THE CARD.
     -----------------------------------------------------------------------
     Adam: "The mobile is too tall and won't fit on a screen in its entirety
     which kind of defeats the purpose of the demo."

     He is right about the consequence and the fix is not to shrink
     everything until it fits. Measured, the card is 1188px against about
     772px of phone above the fold, and the ingredient rows are 394 of that.
     Those rows cannot be compressed without putting the slider back on a
     shared line, which was rejected for a good reason recorded below: at
     three columns the slider gets about 90px, or 20g per thumb width.

     What actually has to be co-visible is a slider and the score it moves.
     Not the whole card. So the verdict strip sticks to the bottom of the
     viewport while the card is in view: drag any ingredient and the HFSS
     and Nutri-Score readings are on screen changing. It returns to the flow
     at the end of the card, so the note and the standing caveat below it are
     never permanently covered.

     The two HFSS models share a row here rather than spanning one each,
     which is what makes the strip short enough to pin without eating the
     screen. */
  .cnl .cnl-metrics--verdicts {
    position: sticky;
    bottom: 0;
    z-index: 1;
    grid-template-columns: repeat(2, 1fr);
    background: var(--bg-surface);
    padding: 8px 0;
    margin-inline: -2px;
  }
  .cnl .cnl-metric--verdict { grid-column: auto; }
  /* Nutri-Score keeps the full width: its A to E scale is the part worth
     showing at size, and it does not fit beside a sibling. */
  .cnl .cnl-metric--nutri { grid-column: 1 / -1; }
  /* The dot sits in the card's padding at desktop; there is none to spare
     here, so it moves inside the row. */
  .cnl .cnl-demo__row--changed::before { left: -12px; }
  .cnl .cnl-demo__rows { padding-left: 12px; }

  /* Name and amount on one line, slider across the full width beneath. Three
     columns at 350px leaves the slider about 90px, which is 20 g per thumb
     width and unusable; this way it gets the whole card. */
  .cnl .cnl-demo__row {
    grid-template-columns: 1fr auto;
    gap: 2px 12px;
    padding: 10px 0;
  }
  .cnl .cnl-demo__slider { grid-column: 1 / -1; grid-row: 2; }
  .cnl .cnl-demo__qty { text-align: right; }

  /* Air taken out of everything that is not the slider. Each of these is
     small; together they are about 120px, which is the difference between
     one ingredient visible with its score and three. */
  .cnl .cnl-demo__name { line-height: 1.3; }
  .cnl .cnl-demo__card .cnl-metric { padding: 10px 12px 8px; }
  .cnl .cnl-demo__note { font-size: 12px; }
  /* The standing caveat was inheriting a 16px body size here, against 12px
     on desktop, so the smallest print on the card was rendering larger than
     the note above it. */
  .cnl .cnl-demo__foot .standing-caveat,
  .cnl .cnl-demo__card .standing-caveat { font-size: 12px; line-height: 1.45; }

  .cnl .cnl-metrics--four { grid-template-columns: repeat(2, 1fr); }
  /* The cost line keeps one row here too: 12px of side padding instead of
     14 so the label and the value clear each other at 350px. */
  .cnl .cnl-cost { padding: 6px 12px; }

  .cnl .cnl-seal { width: 172px; height: 172px; margin: 28px auto 0; }
}

/* --------------------------------------------------------------------------
   Tablet
   --------------------------------------------------------------------------
   701px to 1199px: every iPad in portrait (mini 744, 10.2in 810, 10.9in 820,
   Pro 11in 834, Pro 12.9in 1024) and every iPad in landscape short of the
   12.9in (1080, 1112, 1180).

   The top of this range was 1023 and that was wrong. The desktop hero puts
   the copy beside the stations, and that pair needs about 1200px before it
   fits: 560 of copy, 453 of stations, 128 of gutters, plus a gap. At 1024 the
   stations ran off the right edge and the last two were cut in half, which is
   what a 12.9in iPad in portrait showed. It was checked at 1024 before
   shipping, but the check looked at the logo and pricing rows rather than the
   hero, so it confirmed the wrong thing.

   This block builds on DESKTOP, not on mobile. That is the whole point of
   the split: the phone rules answer a 390px column, and inherited at 820px
   they gave full-bleed buttons, a headline two steps too small and a burger
   menu covering for links that fit with room to spare.

   What actually needs to change at this width is narrower than it looks.
   The nav, the button sizing and most type come back from desktop for free.
   Only the layouts that assume roughly 1300px of room have to be restated,
   and the hero is the main one: copy beside the stations needs about 1000px
   before either stops being cramped, so it stacks here while keeping its
   desktop proportions. Stacking the hero also re-points the scrim, since a
   horizontal ramp guards the wrong axis once the copy sits above the rings
   rather than beside them.
   -------------------------------------------------------------------------- */

@media (min-width: 701px) and (max-width: 1199px) {
  .cnl { --cnl-gut: 36px; }

  /* Hero ------------------------------------------------------------- */
  .cnl .cnl-hero { min-height: 680px; }

  .cnl .cnl-hero__inner {
    flex-direction: column;
    align-items: stretch;
    gap: 54px;
    padding-top: 92px;
    padding-bottom: 96px;
  }

  /* Vertical, because the copy now sits ABOVE the densest part of the ring
     field rather than to its left. */
  .cnl .cnl-hero__scrim {
    background: linear-gradient(
      to bottom,
      color-mix(in srgb, var(--text-primary) 96%, transparent) 0%,
      color-mix(in srgb, var(--text-primary) 88%, transparent) 38%,
      color-mix(in srgb, var(--text-primary) 66%, transparent) 58%,
      color-mix(in srgb, var(--text-primary) 8%, transparent) 78%,
      transparent 100%);
  }

  .cnl .cnl-hero__copy { flex: 1 1 auto; max-width: 640px; }
  .cnl .cnl-hero__headline { font-size: 46px; line-height: 1.02; letter-spacing: -0.02em; }
  .cnl .cnl-hero__lede { max-width: 560px; }

  .cnl .cnl-stations { gap: 17px; }
  .cnl .cnl-stations::before { inset: -120px -110px; }
  .cnl .cnl-station { gap: 16px; }
  .cnl .cnl-station__n { font-size: 21px; }
  .cnl .cnl-station__label { font-size: 30px; }

  /* Counters: four across still fits, and two-by-two would leave the row
     half empty at this width. */
  .cnl .cnl-counters__grid { gap: 18px; }
  .cnl .cnl-counter__value { font-size: 30px; }

  /* Body sections ---------------------------------------------------- */
  .cnl .cnl-why-now { padding: 84px var(--cnl-gut); }
  .cnl .cnl-why-now__headline { font-size: 34px; }

  .cnl .cnl-bench { padding: 80px var(--cnl-gut); }
  .cnl .cnl-bench__headline { font-size: 36px; }

  .cnl .cnl-substrate, .cnl .cnl-security, .cnl .cnl-proof, .cnl .cnl-pricing {
    padding: 80px var(--cnl-gut);
  }

  /* The argument column is a fixed 520px on desktop, which would leave the
     facts about 160px here. Stack them and let the facts pair up instead. */
  .cnl .cnl-demo__inner { grid-template-columns: 1fr; gap: 32px; }
  .cnl .cnl-demo__headline { font-size: 36px; }
  .cnl .cnl-substrate__inner { grid-template-columns: 1fr; gap: 40px; }
  .cnl .cnl-substrate__headline { font-size: 36px; }
  .cnl .cnl-substrate__body { max-width: 68ch; }
  .cnl .cnl-substrate__facts { grid-template-columns: repeat(2, 1fr); gap: 16px; }

  .cnl .cnl-security__inner {
    grid-template-columns: 1fr 1fr;
    grid-template-areas: "argument refusals" "specs specs";
    gap: 36px 32px;
  }
  .cnl .cnl-security__headline { font-size: 36px; }
  .cnl .cnl-method__headline { font-size: 36px; }
  /* Two by two, not three across: there are four specs, so three columns
     strands the fourth on a row of its own. */
  .cnl .cnl-specs { grid-template-columns: repeat(2, 1fr); gap: 26px 32px; }

  .cnl .cnl-proof__headline { font-size: 36px; }
  /* Five logos across 780px is about 140px each, which shrinks the wider
     wordmarks to nothing. Three and two reads as a deliberate pair of rows
     rather than an orphan. */
  .cnl .cnl-logos { grid-template-columns: repeat(3, 1fr); gap: 12px; }

  /* Three quote cards at this width are about 240px each, which breaks the
     quotes into ribbons. The scroller the phone uses is the better answer
     here too, with cards wide enough to read. */
  .cnl .cnl-quotes {
    grid-template-columns: none;
    display: flex;
    gap: 16px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 4px;
  }
  .cnl .cnl-quote { flex: 0 0 340px; scroll-snap-align: start; }

  .cnl .cnl-pricing__headline { font-size: 36px; }
  /* Four tiers across would be 180px each. Two by two keeps the price and
     the feature list readable. */
  .cnl .cnl-tiers { grid-template-columns: repeat(2, 1fr); gap: 16px; }

  .cnl .cnl-news { padding: 72px var(--cnl-gut); }
  .cnl .cnl-close { padding: 88px var(--cnl-gut); }
  .cnl .cnl-close__headline { font-size: 36px; }

  .cnl .footer--columns { padding: 56px var(--cnl-gut); }
}

/* --------------------------------------------------------------------------
   Reduced motion
   --------------------------------------------------------------------------
   The ring field must carry its whole argument with motion disabled. The
   JS reads this too and renders the settled field without animating; the
   rule here is the belt to that braces.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  /* This used to force animation and transition duration to 0.001ms on
     .cnl *, which is the blanket every reduced-motion guide warns about. It
     guarded NOTHING: this stylesheet has no @keyframes at all. The ring
     field is driven by canoli-rings.js, which reads the same preference and
     renders the settled field without ever starting. What the blanket did
     reach was the only two transitions here, button hover and the station
     highlight, so it removed feedback rather than motion.

     What is left disables MOVEMENT and keeps colour. Colour change is not a
     vestibular trigger, and it is the part that tells you a control
     responded. */
  .cnl .cnl-station__label {
    transition: color 0.32s ease;
  }

  /* The burger's bars now rotate into an X. That is movement, and it is the
     one transform this stylesheet adds, so it is the one thing this block
     genuinely has to guard. The X still forms: it simply arrives rather than
     swings, which keeps the close affordance that was the point of adding
     it. */
  .cnl .cnl-burger__bar { transition: none; }

  /* The comparison caret rotates on open. Same reasoning: the state change
     still happens, it just does not travel. */
  .cnl .cnl-compare__summary::after { transition: none; }
}

/* The stations, when nothing is cycling them.
   ---------------------------------------------------------------------------
   canoli-rings.js adds this class when it declines to animate, which today is
   the reduced-motion case. Deliberately NOT inside the reduced-motion media
   query: the class is already the gate, and gating it twice would mean the
   rules could never be exercised or tested except under an emulated media
   feature. One gate, and the state is reachable and verifiable.

   Without it, whichever station carries the active class in the markup sits
   lit for ever and reads as "you are here" on a list where nothing is
   current. All four render alike, and at full strength rather than uniformly
   dimmed, because this list is content and should stay legible. */
.cnl .cnl-stations--static .cnl-station__label {
  transform: none;
  color: var(--text-on-dark);
}
.cnl .cnl-stations--static .cnl-station__n {
  color: var(--text-on-dark-subtle);
}

/* ==========================================================================
   Rename explainer (/why-canoli). Figma: "16 Why Canoli / marketing page ·
   CANONICAL" (1135:26). Reuses the hero, fact, close and button rules above;
   these are only the page's own pieces.
   ========================================================================== */
.cnl .cnl-rename__hero {
  /* The homepage hero is a 760px flex plane; this one is a band that ends
     part-way down the tiles. A hard-stop gradient draws the band, and the
     last 70px show the page ground so the tiles straddle the edge. */
  min-height: 0;
  display: block;
  padding: 88px var(--cnl-gut) 0;
  background: linear-gradient(to bottom,
    var(--text-primary) calc(100% - 70px),
    transparent calc(100% - 70px));
}
/* Atom watermark, cream through the mask at 6%, bleeding off the right. */
.cnl .cnl-rename__mark {
  position: absolute;
  top: -160px;
  right: -220px;
  width: 720px;
  height: 720px;
  background: var(--text-on-dark);
  opacity: 0.06;
  -webkit-mask: url("/img/canoli/atom-mask.svg") center / contain no-repeat;
  mask: url("/img/canoli/atom-mask.svg") center / contain no-repeat;
  pointer-events: none;
}
.cnl .cnl-rename__hero-inner {
  position: relative;
  z-index: 2;
  max-width: 720px;
  margin: 0 auto;
  text-align: center;
}
.cnl .cnl-rename__pair {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 24px;
  margin: 22px 0 0;
}
/* Sized by WIDTH, not height: marketing.css forces height:auto on every
   image at (0,3,1), which outranks any class here. Width is not touched by
   that rule, and both marks keep their aspect, so width is the lever. */
.cnl .cnl-rename__former { width: 150px; opacity: 0.7; }
.cnl .cnl-rename__new { width: 176px; }
.cnl .cnl-rename__arrow { font-size: 28px; color: var(--text-on-dark-subtle); }
.cnl .cnl-rename__headline { font-size: 60px; margin-top: 20px; }
.cnl .cnl-rename__lede { max-width: 620px; margin-left: auto; margin-right: auto; }

.cnl .cnl-rename__tiles {
  position: relative;
  z-index: 2;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  max-width: var(--cnl-max);
  margin: 64px auto 0;
  padding: 0;
}
.cnl .cnl-rename__tile { padding: 22px 24px; }
.cnl .cnl-rename__tile-label,
.cnl .cnl-rename__card-title {
  display: flex;
  align-items: center;
  gap: 8px;
}
/* The butter dot: the rail's "changed" marker, in front of every label. */
.cnl .cnl-rename__tile-label::before,
.cnl .cnl-rename__card-title::before {
  content: "";
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-button);
  background: var(--brand-gold);
}
.cnl .cnl-rename__card-title::before { width: 10px; height: 10px; }
.cnl .cnl-rename__tile-label {
  margin: 0;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--decorative-muted);
}
.cnl .cnl-rename__tile-big {
  margin: 6px 0 0;
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--text-primary);
}
.cnl .cnl-rename__tile .cnl-fact__body { color: var(--decorative-muted); }

.cnl .cnl-rename__cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  max-width: var(--cnl-max);
  margin: 48px auto 72px;
  padding: 0 var(--cnl-gut);
}
.cnl .cnl-rename__card { padding: 24px 26px 26px; }
.cnl .cnl-rename__card-title { font-size: 22px; }
.cnl .cnl-rename__card .cnl-fact__body { font-size: 15px; margin-top: 10px; }
.cnl .cnl-rename__mail { color: inherit; text-decoration: underline; text-underline-offset: 3px; }

@media (max-width: 700px) {
  .cnl .cnl-rename__hero { padding: 56px var(--cnl-gut) 0; }
  .cnl .cnl-rename__mark { width: 420px; height: 420px; top: -120px; right: -160px; }
  /* Stacked tiles: the band stops around the middle tile rather than
     swallowing two whole cards before the ground shows. */
  .cnl .cnl-rename__hero {
    background: linear-gradient(to bottom,
      var(--text-primary) calc(100% - 200px),
      transparent calc(100% - 200px));
  }
  .cnl .cnl-rename__former { width: 105px; }
  .cnl .cnl-rename__new { width: 122px; }
  .cnl .cnl-rename__arrow { font-size: 20px; }
  .cnl .cnl-rename__headline { font-size: 36px; }
  .cnl .cnl-rename__tiles { grid-template-columns: 1fr; gap: 12px; margin-top: 40px; }
  .cnl .cnl-rename__cards { grid-template-columns: 1fr; margin: 32px auto 56px; }
}
@media (min-width: 701px) and (max-width: 1199px) {
  .cnl .cnl-rename__tiles { grid-template-columns: repeat(3, 1fr); }
}
