/* ============================================================================
   SERIES 01 — editorial concept for the Nippon Paint UAE range.

   The layout, type and grid are the concept's own and share nothing with
   ../site/assets/css/site.css. The *colour* is the brand's: this file imports
   the design system's own colour tokens and builds its ramp out of them, so
   every value on the page is a real Nippon colour rather than a steel blue
   picked to match a reference screenshot. Change the token file and this page
   follows.

   Carried over from the reference: the typography with its bracketed
   micro-labels, and products standing on tinted panels.
   ========================================================================= */

@import url("../site/assets/tokens/colors.css");
@import url("../site/assets/tokens/fonts.css");
@import url("../site/assets/tokens/typography.css");

:root{
  /* The brand blue ramp, in the concept's own value order.
     The reference is monochrome — one hue, many values — and Nippon's identity
     colour is a blue, so the two agree. Where the concept needs a value the
     brand ramp does not carry, the nearest token is reused rather than a new
     hex invented; the names below are positions in this page's ramp, not new
     colours.

     Deliberately built from the *deep* end (950-700) for the ground and the
     lighter end (300-500) for the panels: that is how the live site already
     uses brand blue — deep field, brand blue furniture. Mapping the page ground
     to --np-blue-500 turned the whole thing a vivid azure that appears nowhere
     in the system. */
  --ice-950:var(--np-blue-950);   /* #071E42 */
  --ice-900:var(--np-blue-900);   /* #0A2C5C — the site's own deep brand field */
  --ice-850:var(--np-blue-900);
  --ice-800:var(--np-blue-800);   /* #0F3A76 */
  --ice-700:var(--np-blue-700);   /* #15468B — the identity blue */
  --ice-600:var(--np-blue-600);   /* #1D5AAD */
  --ice-500:var(--np-blue-500);   /* #2A72CE */
  --ice-400:var(--np-blue-500);   /* panels lean on 500/300 */
  --ice-300:var(--np-blue-300);   /* #8FB4E2 */
  --ice-200:var(--np-blue-100);   /* #DDE8F6 — dim text on the deep ground */
  --ice-100:var(--np-blue-100);
  --ice-050:var(--np-white);

  /* Accent. The emblem's "n" is red; the design system allows it as an accent
     and never as a field or as body text. It appears in exactly two places
     here: the marker under a selected option, and the add confirmation. */
  --accent:var(--np-red-500);

  /* One panel gradient for the whole page, declared once.
     There were four of these, hand-tuned per component: three saturated and
     dark, one light. Two unrelated blues in the same eyeline is what made the
     page feel unsettled — the deep ground and the panels were arguing instead
     of sitting in one family. Now it is one relationship, repeated: a deep
     ground, and panels held in the light half of the brand ramp. The cans are
     black, so light panels also give them the most to sit against, and the only
     panel that carries text keeps its contrast without being a special case. */
  --panel:linear-gradient(165deg,
    var(--np-blue-100) 0%, var(--np-blue-300) 74%, var(--np-blue-500) 100%);

  /* The panel that carries body text — the colour picker, the basket drawer and
     Adam's window. Held in the top half of the ramp rather than running to
     blue-500 like --panel above: that gradient is right behind a black tin and
     one line of type, and wrong under a panel that is nothing but text. The dim
     micro type down at its dark end measured 2.9:1 before this existed. */
  --panel-text:linear-gradient(165deg,var(--np-blue-100) 0%,var(--np-blue-300) 100%);

  --ink:var(--ice-950);
  --paper:var(--ice-050);

  /* The ink as channels, so every rgba() built from it can be moved by a theme
     in one line. Sixty-one places in this file mix the ink at some opacity —
     dim labels, hairlines, hover fills — and a dark theme has to lift all of
     them together or none.

     --shadow-rgb is the same triple and is deliberately not the same token: a
     shadow stays dark whatever the theme is doing. Lifting those with the ink
     would have turned every drop shadow into a glow. */
  --ink-rgb:7,30,66;
  --shadow-rgb:7,30,66;

  /* No font declarations here on purpose. --font-display, --font-sans and
     --font-mono all come from the imported typography.css, which states the
     system's rule outright: one family (Poppins) carries everything, and weight
     and case do the work.

     That rule costs this page something and it is worth naming. The reference's
     headline is a narrow, heavy condensed face; Poppins is a wide geometric one.
     Set at the old size the product names ran off the column, so the display
     scale below is the system's own --fs-hero rather than a bigger invented one,
     and the compressed-headline feel of the reference is gone. That is what
     using the brand face means.

     One exception, marked where it happens: the stock code keeps --font-mono.
     It is a machine string of digits and dashes, and a proportional face makes
     it materially harder to read off the screen and check against a can. */

  /* The campaign spectrum, declared once. It is what the NIPPON UAE lettering is
     poured out of, and it is used in exactly two places that both have to match
     it: the wave along the bottom of the bar, and the loading stripe. Two
     hand-copied gradients would have drifted the first time a stop moved.
     Magenta opens and closes it, so a 200%-wide draw loops with no seam. */
  /* The two splash backdrops, named once. Which one a panel gets and how far
     its hue is turned are decided per product in concept.js — the URLs stay
     here so the version marker lives in one file. */
  --splash-a:url("assets/splash-1.webp?v=1");
  --splash-b:url("assets/splash-2.webp?v=1");

  --spectrum:linear-gradient(90deg,
    #E52E8B 0%, #F0592B 14%, #F5B31B 28%, #57B947 43%,
    #16A9C9 57%, #2F6FD0 71%, #8C4BC9 86%, #E52E8B 100%);

  --gap:clamp(10px,1.1vw,18px);
  --pad:clamp(16px,3vw,44px);
  --radius:3px;               /* near-square. The reference has almost no rounding */

  --ease:cubic-bezier(.16,1,.3,1);
  --dur:220ms;
}

*,*::before,*::after{box-sizing:border-box}

html{-webkit-text-size-adjust:100%}

body{
  margin:0;
  background:var(--ice-900);
  color:var(--paper);
  font-family:var(--font-sans);
  font-size:13px;
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
}

img{max-width:100%;display:block}
svg{width:100%;height:100%;fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round}

/* The whole page sits on one cold field with a light source top-left, the way
   the reference's mountain photograph lifts the top of the composition. */
.frame{
  min-height:100vh;
  background:
    /* Light source top-left. */
    radial-gradient(120% 80% at 8% -10%, rgba(29,90,173,.75) 0%, transparent 62%),
    /* The tint. Heavy on purpose: the photograph is here for texture and depth,
       not as a picture of the sea — anything lighter and a page of paint cans
       ends up sitting on a holiday snap. The brand field still reads as the
       brand field, with something moving underneath it. */
    linear-gradient(180deg, rgba(15,58,118,.90) 0%, rgba(10,44,92,.94) 46%, rgba(var(--ink-rgb),.97) 100%),
    /* Fixed, so the surf stays put and the page moves over it. */
    url("assets/bg-sea.webp") center / cover fixed no-repeat,
    /* Fallback colour for the moment before the image decodes. */
    var(--np-blue-900);
  padding:0 var(--pad) var(--pad);
}
/* background-attachment:fixed is expensive on a phone — iOS in particular
   repaints the whole layer on every scroll frame. The texture is subtle enough
   under this tint that scrolling with the page costs nothing visually. */
@media (max-width:900px){
  .frame{background-attachment:scroll,scroll,scroll}
}

/* ── Typography ──────────────────────────────────────────────────────────────
   All from the design system: Poppins, the system's own size steps and its own
   tracking. Poppins is a wide geometric face where the reference used a narrow
   condensed one, so the display scale is --fs-hero rather than the larger size
   this page carried before — at 86px the product names ran past the column.
   Weight and case do the work instead of width, which is the rule the system
   states. */
.display{
  font-family:var(--font-display);
  font-weight:var(--fw-bold);
  text-transform:uppercase;
  letter-spacing:var(--ls-tight);
  line-height:.98;
  margin:0;
  font-size:var(--fs-hero);
}
.display__line{display:block}
.display sup{font-size:.34em;vertical-align:super;font-weight:var(--fw-semibold);letter-spacing:0}
.display--section{font-size:var(--fs-display-2)}

/* The bracketed labels. Everything technical on the page is uppercase and widely
   tracked at the system's own --ls-eyebrow — that texture is what makes the
   display type read as a headline rather than as decoration. It was a monospace
   before the brand face came in; the tracking now carries that job alone. */
.micro{
  font-family:var(--font-sans);
  font-size:var(--fs-micro);
  font-weight:var(--fw-semibold);
  letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase;
  margin:0;
}
.micro--dim{color:var(--ice-200)}

.price{
  font-family:var(--font-display);
  font-weight:var(--fw-semibold);
  font-size:clamp(22px,2.4vw,30px);
  letter-spacing:var(--ls-snug);
  margin:2px 0 0;
}

/* ── Bar ─────────────────────────────────────────────────────────────────────
   A white strip the full width of the window. It sits outside .frame so it can
   bleed to both edges while the page's side padding lives on the inner row —
   inside .frame it would have been inset by that padding and read as a card. */
/* Plain white, and it stays that way.

   Two pictures have been tried in here — the paint wave and the landmarks
   panorama — and both were the same mistake. At 68px a picture is a 68px crop
   of a picture, and this bar already has a logo, six links and three controls
   in it. */
.bar{position:relative;background:var(--np-white)}

/* The stripe along the bottom edge of the bar: the campaign spectrum, straight.

   It was a wave for a few builds — a stroked SVG path masked into a ribbon with
   the white above it following the same curve — and it is a straight line
   again. The wave is still the right idea in the abstract; on the top edge of
   every page, permanently in view, it was one shape too many.

   --spectrum is the same variable the loading stripe reads, so the two can
   never drift apart. It replaces the bar's hairline rather than sitting under
   it: two lines would read as a border with a stripe stuck to it. */
.bar::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:5px;
  background:var(--spectrum);
}

@keyframes bar-wave{ to{ background-position:-200% 0 } }


/* ── Dark ─────────────────────────────────────────────────────────────────
   The same page after dark. Nothing here is a new colour invented for the
   occasion: the field drops to the bottom of the brand ramp, the panels move
   from the top of it to the bottom, and the ink and paper swap ends.

   --ink-rgb is the piece that does most of the work. Forty-eight rgba() values
   in this file are the ink at some opacity — dim labels, hairlines, hover
   fills, form borders — and they all read that one triple, so they lift
   together instead of forty-eight rules needing a dark twin.

   The white bar goes near-black and the logo takes the plate the design system
   asks for on a dark ground. That is the same treatment the footer has always
   used; it is not a special case invented here.

   The artwork does not change. The lettering, the splashes and the panorama are
   the campaign, they were cut out on transparency, and they sit on whatever
   they are put on. */
:root[data-theme="dark"]{
  --ink-rgb:216,230,248;

  /* The field: below the ramp's darkest step, because a dark theme whose
     background is the light theme's background is not a dark theme. */
  --ice-950:#02070F;
  --ice-900:#050D1B;
  --ice-850:#071228;
  --ice-800:#0A1A34;
  --ice-700:#0E2444;
  --ice-600:#123057;
  --ice-500:#1A3F6E;
  --ice-400:#1A3F6E;
  --ice-300:#5E86BC;
  --ice-200:#9DB6D8;
  --ice-100:#C8D8EE;
  --ice-050:#EAF1FB;

  /* Panels move to the bottom of the ramp and their type inverts with them. */
  --panel:linear-gradient(165deg,#0E2444 0%,#0A1A34 74%,#071228 100%);
  --panel-text:linear-gradient(165deg,#12294C 0%,#0B1B33 100%);
  --ink:#E4EEFC;
  --paper:#EAF1FB;
}

/* The field's own layers. The photograph stays — it is texture, not a picture —
   but the tint over it goes to the new ground, and the light source at the top
   left is dimmed: at full strength it read as a sunrise in the corner of a
   night page. */
:root[data-theme="dark"] .frame{
  background:
    radial-gradient(120% 80% at 8% -10%, rgba(26,63,110,.55) 0%, transparent 62%),
    linear-gradient(180deg, rgba(10,26,52,.92) 0%, rgba(5,13,27,.95) 46%, rgba(2,7,15,.98) 100%),
    url("assets/bg-sea.webp") center / cover fixed no-repeat,
    var(--ice-900);
}

/* The bar. Near-black rather than the field's own colour, so the boundary
   between the two is still readable when both are dark. */
:root[data-theme="dark"] .bar{background:#080C14}
:root[data-theme="dark"] .bar__nav a{color:var(--ice-100)}
:root[data-theme="dark"] .disc{border-color:rgba(var(--ink-rgb),.28);color:var(--ice-100)}
:root[data-theme="dark"] .disc:hover{background:rgba(var(--ink-rgb),.1)}
:root[data-theme="dark"] .lang__f:hover{background:rgba(var(--ink-rgb),.12)}
/* No plate under the logo after dark, at the client's request — in the bar or
   in the footer.

   It is worth writing down what that costs. The lockup is brand blue and red on
   transparent, the design system does not allow it to be recoloured, and the
   system's own answer for a dark ground is the white plate that has just been
   taken away. Blue on near-black is a dim pairing; it is legible at this size
   and the red mark carries it, and if the brand kit has a reverse version this
   is the third place it belongs. Recolouring the supplied file to white was the
   alternative, and that is the thing the system actually forbids. */
:root[data-theme="dark"] .brand--foot{background:none;padding:0}
:root[data-theme="dark"] .lang__f svg{box-shadow:inset 0 0 0 1px rgba(var(--ink-rgb),.3)}

/* The feature tile's text column. --panel inverted with the theme and this did
   not: the name, the spec values and the price are all --ice-950, which is the
   near-black kept for text on light chips and which the dark theme keeps dark
   on purpose. On the inverted panel that measured 1.1:1 — the words were there
   and could not be read. Ordinary tiles were never affected; they carry the
   inherited --paper. */
:root[data-theme="dark"] .feature__side,
:root[data-theme="dark"] .feature__name,
:root[data-theme="dark"] .feature__price,
:root[data-theme="dark"] .spec--light dd{color:var(--ice-050)}

/* Anything that was a white surface in the light theme. */
:root[data-theme="dark"] .sheet{background:#0A1120;color:var(--ice-050)}
:root[data-theme="dark"] .fld input,
:root[data-theme="dark"] .fld select,
:root[data-theme="dark"] .fld textarea,
:root[data-theme="dark"] .adam-input,
:root[data-theme="dark"] .qnum{background:rgba(var(--ink-rgb),.08);color:var(--ink)}
:root[data-theme="dark"] .adam-fab{background:#0E2444;color:var(--ice-050);
  border-color:rgba(var(--ink-rgb),.22)}
:root[data-theme="dark"] .adam-fab:hover{background:#123057}
:root[data-theme="dark"] .adam-bot{background:rgba(var(--ink-rgb),.1);color:var(--ink)}
:root[data-theme="dark"] .adam-me{background:#1A3F6E;color:var(--ice-050)}
:root[data-theme="dark"] .adam-head{background:rgba(var(--ink-rgb),.06)}
:root[data-theme="dark"] .pill--solid{background:var(--ice-100);color:#02070F;
  border-color:var(--ice-100)}
:root[data-theme="dark"] .pill--solid:hover{background:var(--ice-050)}
:root[data-theme="dark"] .filters .pill[aria-pressed="true"]{background:var(--ice-100);
  color:#02070F}
:root[data-theme="dark"] .pick .filters .pill[aria-pressed="true"],
:root[data-theme="dark"] .pickp__packs .pill[aria-pressed="true"],
:root[data-theme="dark"] .cform__kind .pill[aria-pressed="true"]{
  background:var(--ice-100);color:#02070F;border-color:var(--ice-100)}
:root[data-theme="dark"] .adam-send{background:#1A3F6E}
:root[data-theme="dark"] .adam-send:hover{background:#25518A}

/* The opening screen is black in both themes — it was drawn that way. */
/* ── The opening screen ───────────────────────────────────────────────────
   Black, the logo, the campaign artwork and the loading bar. It is in the
   markup rather than built by script, so it is on screen from the first paint
   instead of after a round trip.

   Pure black, not near-black: the artwork was supplied on #000 and cut off it,
   and on the exact same black the edges of the cut are invisible. A shade or
   two away and the spatter round the outside starts showing a faint halo where
   the key softened.

   The logo stands on the black with no plate under it. It is the one thing here
   that is not obviously right: the lockup is brand blue and red on transparent,
   the design system does not allow it to be recoloured, and blue on black is a
   dim pairing. It is legible at this size and the red mark carries it, but if
   the brand kit has a reverse version this is where it belongs. Recolouring the
   supplied one to white was the alternative, and that is the thing the system
   actually forbids.

   Two ways out, and it needs both. The script takes it off on window.load,
   which is the normal path. The CSS animation is the safety net: with no
   script, a broken script or a page that never fires load, the screen still
   clears itself after three and a half seconds. A splash that cannot clear
   itself is a site nobody can use, and that failure has to be impossible
   rather than unlikely. */
.splash{
  position:fixed;inset:0;z-index:90;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:clamp(18px,3.5vh,44px);
  padding:clamp(20px,5vw,60px);
  background:#000;
  animation:splash-out 460ms var(--ease) 3200ms forwards;
}
.splash__logo{width:min(132px,26vw);height:auto}
.splash__art{
  width:min(860px,100%);height:auto;
  animation:splash-rise 900ms var(--ease) both;
}
/* Taken off by the script; the animation above is only the fallback. */
.splash.is-done{animation:splash-out 460ms var(--ease) forwards}
@keyframes splash-out{ to{ opacity:0; visibility:hidden } }
@keyframes splash-rise{ from{ opacity:0; transform:translateY(14px) scale(.985) } }

@media (prefers-reduced-motion:reduce){
  .splash__art{animation:none}
  .splash{animation-duration:1ms}
  .splash.is-done{animation-duration:1ms}
}

/* ── The loading bar ──────────────────────────────────────────────────────
   Inside the opening screen, under the artwork. It was a hairline across the
   top of the window; it is a bar in the middle of the screen now, where the
   waiting is actually being done.

   Still driven entirely by CSS, so it starts filling the moment the parser
   reaches it — earlier than any script could start it. The script only finishes
   it off on window.load.

   It creeps to 92% and waits there. A loading bar cannot honestly know how far
   through it is: the browser reports bytes for one request at a time, not for
   the page. So it does what every honest one does — moves quickly at first,
   slows, and is finished off by the load event rather than pretending to
   measure. */
.load{
  width:min(300px,64vw);height:4px;border-radius:999px;overflow:hidden;
  /* The unfilled part of the track, on black. It was a tint of the ink, which
     was right on white and invisible on the screen it actually sits on. */
  background:rgba(255,255,255,.16);
  pointer-events:none;
  transition:opacity 380ms var(--ease) 140ms;
}
.load__bar{
  display:block;height:100%;
  background:var(--spectrum);background-size:200% 100%;
  transform-origin:0 50%;
  animation:load-creep 9s cubic-bezier(.12,.86,.28,1) forwards,
            bar-wave 4s linear infinite;
}
@keyframes load-creep{ from{ transform:scaleX(.04) } to{ transform:scaleX(.92) } }

/* Done: drop the creep from the list so the animation stops holding the
   transform, and let the transition carry the last stretch to full. */
.load.is-done{opacity:0}
.load.is-done .load__bar{
  animation:bar-wave 4s linear infinite;
  transform:scaleX(1);
  transition:transform 240ms var(--ease);
}
@media (prefers-reduced-motion:reduce){
  /* A full stripe that fades when the page is ready: the same information,
     with nothing crawling across the top of the screen. */
  .load__bar{animation:none;transform:scaleX(1)}
  .load.is-done .load__bar{animation:none}
}
.bar__inner{
  display:flex;align-items:center;gap:var(--gap);
  padding:14px var(--pad);
}
/* The logo. It is brand blue on transparent and may not be recoloured, so it
   needs a white ground: in the bar that is the strip itself, in the footer —
   which sits on the deep blue field — it is a plate of its own, the same way the
   live site's footer handles it.

   .brand, not .mark: .mark was already the seal chip, and reusing the name
   painted every certification chip white and left its near-white label
   invisible on it. */
.brand{display:inline-flex;flex:0 0 auto;text-decoration:none}
.brand img{height:40px;width:auto}
.brand--foot{
  background:var(--np-white);border-radius:var(--radius);padding:11px 16px;
}
.brand--foot img{height:44px}
/* Wider gap than when these were bracketed. The brackets were doing the
   separating; without them, five tracked-out uppercase words in a row start
   to read as one string. */
.bar__nav{display:flex;gap:clamp(16px,2.1vw,36px);flex:1;justify-content:center;flex-wrap:wrap}
/* On the white strip the bar's own text and controls take the identity blue —
   the near-white they carried on the blue ground would have been invisible. */
.bar__nav a{
  font-family:var(--font-sans);font-size:11px;font-weight:var(--fw-semibold);
  letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase;color:var(--np-blue-700);text-decoration:none;
  transition:color var(--dur) var(--ease);
}
.bar__nav a:hover{color:var(--np-blue-900)}
.bar__utils{display:flex;gap:8px;flex:0 0 auto}

.disc{
  width:34px;height:34px;padding:8px;flex:0 0 auto;
  border:1px solid var(--np-blue-300);border-radius:50%;
  background:transparent;color:var(--np-blue-700);cursor:pointer;
  transition:background-color var(--dur) var(--ease),border-color var(--dur) var(--ease);
}
.disc:hover{background:var(--np-blue-50);border-color:var(--np-blue-700)}

/* The basket, bigger and with its name on it.

   Everything else in this corner is a symbol somebody has seen a thousand times
   — a magnifier, a sun. A basket icon is recognisable too, but it is the one
   control here that a visitor goes looking for, and the one whose state matters
   (there may be four tins in it). So it gets the pill treatment: taller, wider,
   and it says what it is.

   The label goes at the narrow end of the bar, where the navigation has already
   collapsed and every pixel is spoken for. */
.disc--cart{
  width:auto;height:40px;padding:0 16px 0 12px;border-radius:999px;
  display:inline-flex;align-items:center;gap:8px;
}
.disc--cart svg{width:20px;height:20px;flex:0 0 auto}
.disc__t{
  font:var(--fw-semibold) var(--fs-micro)/1 var(--font-sans);
  letter-spacing:.1em;text-transform:uppercase;white-space:nowrap;
}
.disc--cart .disc__n{top:-4px;right:-4px}
@media (max-width:720px){
  .disc__t{display:none}
  .disc--cart{width:34px;padding:0;justify-content:center}
  .disc--cart svg{width:18px;height:18px}
}
.disc:active{transform:scale(.96)}

/* ── Hero ────────────────────────────────────────────────────────────────── */
.hero{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1.15fr) minmax(0,.55fr);
  grid-template-areas:"text panel reel";
  gap:var(--gap);
  align-items:stretch;
  min-height:min(74vh,620px);
}
/* Grouped, not evenly spaced. All eight blocks on one uniform gap read as a
   list of unrelated things; the eye needs to see name-and-label as one unit,
   the two descriptions as another, the choices and specs as a third, and the
   price standing apart. Same total height, four groups instead of eight items. */
.hero__text{
  grid-area:text;
  display:flex;flex-direction:column;justify-content:center;gap:0;
  padding-block:clamp(20px,3vw,44px);
}
.hero__text > #hero-name{margin-top:12px}
.hero__text > #hero-blurb{margin-top:clamp(16px,1.8vw,24px)}
.hero__text > #hero-note{margin-top:10px}
.hero__text > .picker{margin-top:clamp(18px,2.2vw,30px)}
.hero__text > #hero-spec{margin-top:16px}
.hero__text > #hero-marks{margin-top:18px}
.hero__text > .buy{margin-top:clamp(20px,2.6vw,34px)}

/* The panel: product on a tinted ground, the reference's central device. */
.hero__panel{
  grid-area:panel;position:relative;overflow:hidden;border-radius:var(--radius);
  background:var(--panel);
  display:grid;place-items:center;
  min-height:clamp(280px,42vw,560px);
}
/* Oversized wordmark behind the can — the graffiti layer in the reference,
   done with type instead of a photograph we do not have. */
.hero__wash{
  position:absolute;inset:auto -4% 4% -4%;margin:0;
  font-family:var(--font-display);font-weight:700;text-transform:uppercase;
  font-size:clamp(56px,11vw,150px);line-height:.84;letter-spacing:-.02em;
  color:rgba(var(--ink-rgb),.13);
  white-space:nowrap;text-align:center;pointer-events:none;user-select:none;
}
.hero__art{
  position:relative;width:min(86%,440px);height:auto;
  filter:drop-shadow(0 26px 40px rgba(var(--shadow-rgb),.42));
}

.hero__blurb{
  margin:0;max-width:44ch;font-family:var(--font-sans);font-size:var(--fs-caption);
  line-height:1.6;letter-spacing:.01em;color:var(--ice-100);
}
/* The manufacturer's own line, set apart from ours by a rule rather than by a
   label — the distinction matters for sign-off, not to the reader. */
.hero__note{
  margin:0;max-width:44ch;padding-inline-start:12px;
  border-inline-start:1px solid rgba(255,255,255,.3);
  font-family:var(--font-sans);font-size:var(--fs-micro);line-height:1.55;letter-spacing:.02em;
  color:var(--ice-200);
}
.hero__note[hidden]{display:none}

/* ── Seal marks ──────────────────────────────────────────────────────────
   The catalogue prints its certifications as vector medallions with no text
   layer. Label plus a monoline mark, because four lines of small caps in a row
   had to be read word by word before you could tell them apart. */
.marks{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:7px 10px}
/* A product with no certification seals keeps its place in the tile anyway.

   This was display:none, and display:none takes the element out of the grid
   altogether — so that tile lost a row, everything under it moved up one, and
   Calcino's price sat a hundred pixels above the four prices beside it. The
   whole point of the nine-row subgrid is that a row of tiles agrees on where
   each line falls; an element that has vanished cannot agree with anything.

   Empty and displayed it adds no height of its own, and the shared row is sized
   by whichever tile in that row does carry marks. */
.marks--none{display:flex}
.mark{
  display:inline-flex;align-items:center;gap:6px;min-width:0;
  padding:4px 9px 4px 6px;border-radius:999px;
  border:1px solid rgba(255,255,255,.3);
  font-family:var(--font-sans);font-size:10px;font-weight:500;letter-spacing:.1em;
  text-transform:uppercase;color:var(--ice-100);
}
.mark svg{width:13px;height:13px;flex:0 0 auto;stroke-width:1.5}
.marks--light .mark{border-color:rgba(var(--ink-rgb),.28);color:rgba(var(--ink-rgb),.8)}

/* ── Aligned label/value blocks ──────────────────────────────────────────
   Every technical block on the page — the pickers, the hero parameters, the
   range counts, the tile specs — uses the same two-column measure. The label
   column is a fixed width rather than `auto` so the values start on the same
   line whichever block, and whichever tile, they are in. `auto` sized to each
   block's own longest label and nothing lined up with anything. */
:root{--label-w:7.4em}

.spec{margin:0;display:grid;gap:5px 0}
.spec__row{display:grid;grid-template-columns:var(--label-w) minmax(0,1fr);
  gap:0 var(--gap);align-items:baseline}
.spec dt{
  font-family:var(--font-sans);font-size:10px;font-weight:var(--fw-semibold);letter-spacing:.14em;
  text-transform:uppercase;color:var(--ice-200);margin:0;
}
.spec dd{
  margin:0;font-family:var(--font-sans);font-size:11px;font-weight:var(--fw-medium);letter-spacing:.03em;
  color:var(--paper);
}
/* The stock code is the one string that keeps a monospace — the system's own
   --font-mono token, a system stack, not a webfont. It is digits and dashes read
   off the screen and checked against a can, and Poppins makes that materially
   harder: its 1, its 0 and its dashes all sit at different widths. */
.spec--hero .spec__row:last-child dd{
  font-family:var(--font-mono);letter-spacing:var(--ls-code);font-size:11px;
}
.spec--hero{gap:7px 0;padding-block:2px}
.spec--hero dt{font-size:11px}
.spec--hero dd{font-size:12px}
/* The meta block beside a section heading reads across, not down.

   Everywhere else .spec is a stack of label-and-value rows, which is right when
   it sits in a narrow tile column. Here it sits next to a two-line display
   heading in a full-width row, and stacked it made a tall thin list that pushed
   the heading off its own line and left the whole band ragged.

   Label over value, laid out in a row: every label sits on one line and every
   value on the next, so the numbers can be read straight across. --label-w is
   dropped to auto because the fixed label column is what a stacked row needs
   and exactly what a horizontal one must not have. */
.spec--meta{
  --label-w:auto;
  display:flex;flex-wrap:wrap;
  gap:8px clamp(18px,2.4vw,34px);
  padding-bottom:6px;
}
.spec--meta .spec__row{
  display:flex;flex-direction:column;gap:2px;align-items:flex-start;
}
/* On the light feature panel the same block inverts. */
.spec--light dt{color:rgba(var(--ink-rgb),.6)}
.spec--light dd{color:var(--ice-950)}

.picker{margin:0;display:grid;grid-template-columns:var(--label-w) minmax(0,1fr);
  gap:10px 0;align-items:center}
.picker dt{color:var(--ice-200)}
.picker__row{margin:0;display:flex;gap:clamp(10px,1.4vw,22px);flex-wrap:wrap}
.opt{
  border:0;background:transparent;padding:2px 0;cursor:pointer;
  font-family:var(--font-sans);font-size:12px;font-weight:var(--fw-medium);letter-spacing:.06em;
  text-transform:uppercase;color:var(--ice-200);
  border-bottom:1px solid transparent;
  transition:color var(--dur) var(--ease),border-color var(--dur) var(--ease);
}
.opt:hover{color:var(--paper)}
/* The one place the emblem's red earns its keep: which pack, which colour. It
   marks a choice and nothing else — never a field, never body text. */
.opt[aria-pressed="true"]{color:var(--paper);border-bottom-color:var(--accent)}
.opt__sw{
  display:inline-block;width:9px;height:9px;border-radius:50%;margin-inline-end:6px;
  vertical-align:baseline;box-shadow:inset 0 0 0 1px rgba(var(--shadow-rgb),.35);
}

.buy{display:flex;align-items:center;gap:clamp(14px,1.8vw,24px)}

/* ── Reel ────────────────────────────────────────────────────────────────── */
.reel{grid-area:reel;display:flex;flex-direction:column;justify-content:flex-start;
  gap:var(--gap);min-width:0}
/* 2x2, not a column of four. Four cells stacked in this narrow column came out
   at roughly 2.4:1 — wide, short slots beside a near-square hero panel and 3/4
   product tiles. Every other block on the page is portrait or square; these
   were the one shape that did not rhyme with anything. */
.reel__track{
  display:grid;grid-template-columns:1fr 1fr;grid-auto-rows:auto;gap:var(--gap);
  overflow:hidden;
}
.reel__item{
  position:relative;aspect-ratio:4/5;min-height:0;overflow:hidden;
  border:0;padding:0;border-radius:var(--radius);cursor:pointer;
  background:var(--panel);
  display:grid;place-items:center;
  transition:opacity var(--dur) var(--ease);
  opacity:.62;
}
/* Absolutely positioned and object-fit:contain, rather than a percentage size.
   These items are far wider than they are tall, so a percentage *width* made the
   can taller than the panel and overflow:hidden cropped it to a lid. Swapping to
   a percentage max-height did not help either: a percentage height against a
   grid row the browser sizes from its content is indefinite, so it is ignored.
   Filling the box and letting object-fit do the letterboxing always works. */
.reel__item img{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:contain;padding:9% 13% 22%;
  filter:drop-shadow(0 8px 12px rgba(var(--shadow-rgb),.35));
}
.reel__item:hover{opacity:.85}
.reel__item[aria-pressed="true"]{opacity:1;outline:1px solid rgba(255,255,255,.75);outline-offset:-1px}
/* The thumbnails were unlabelled: four near-identical black tubs told you
   nothing about what you were about to switch to. */
/* Two lines, not one with an ellipsis. Truncating from the right cut these
   labels at exactly the word that tells them apart: "Minoa Premium Silk Matt"
   and "Minoa Premium Matt" both came out as "MINOA PREMIUM SILK …". */
.reel__tag{
  position:absolute;left:0;right:0;bottom:0;padding:6px 8px;
  font-family:var(--font-sans);font-size:9px;font-weight:var(--fw-semibold);
  letter-spacing:.08em;line-height:1.25;
  text-transform:uppercase;text-align:start;color:var(--ice-950);
  background:linear-gradient(to top,rgba(255,255,255,.92),rgba(255,255,255,.6) 62%,rgba(255,255,255,0));
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
.reel__count{display:flex;align-items:center;gap:10px;justify-content:flex-end;color:var(--ice-100)}
.reel__count i{display:block;width:34px;height:1px;background:rgba(255,255,255,.5)}

/* ── Range ───────────────────────────────────────────────────────────────── */
.range{padding-top:clamp(26px,3vw,44px)}
/* The counts sit next to the heading, and the button takes the slack.
   This row has been tried three ways: the heading on flex:1 shoved counts and
   button into the right edge; space-between threw the counts to the opposite
   edge and opened a gap across the middle. Grouping the two pieces of the same
   statement and letting the one action float right is the one that reads. */
.range__head{
  display:flex;align-items:flex-start;
  gap:var(--gap);flex-wrap:wrap;
  margin-bottom:clamp(12px,1.4vw,18px);
}
.range__head .display{flex:0 1 auto}
/* Now that the meta reads across, it takes the width it needs rather than being
   held to a 200px column that forced it to wrap into a list again. */
.range__head .spec--meta{flex:0 1 auto;padding-bottom:0}
/* The pill takes the slack, not the heading. With the heading on flex:1 the
   counts and the button were both shoved against the right edge and the middle
   of the row was empty. */
.range__head .pill{margin-inline-start:auto}

.pill{
  padding:9px 22px;border-radius:999px;cursor:pointer;
  border:1px solid rgba(255,255,255,.45);background:transparent;color:var(--paper);
  font-family:var(--font-sans);font-size:11px;font-weight:600;letter-spacing:.16em;
  text-transform:uppercase;
  transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease);
}
.pill:hover{background:var(--paper);color:var(--ice-900)}
.pill:active{transform:scale(.97)}

/* Five columns, with the feature tile spanning two — the reference's rhythm.
   Six rows, and every tile spans all six, so the tiles can share row tracks
   through `grid-template-rows: subgrid`: the name, the description, each spec
   row, the marks and the price then sit on the same line right across a row of
   tiles. Sized per tile, they landed at five different heights — a long product
   name in one tile pushed everything below it down only in that tile.

   Where subgrid is unsupported the declaration is simply dropped and each tile
   sizes its own rows again, which is untidy but not broken. */
.tiles{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:var(--gap)}
/* Nine rows, not six: panel, name, description, the four spec rows, the marks,
   the price. Counting the spec rows individually is the whole point — with the
   block as one row, PACK wrapping to two lines in one tile pushed that tile's
   TINITNG line out of step with its neighbours. The spec opts into the same
   subgrid below, so every parameter sits on its own shared line. */
.tile{
  grid-row:span 9;
  display:grid;grid-template-rows:subgrid;row-gap:0;
  color:var(--paper);min-width:0;
}
.tile > .spec{grid-row:span 4;grid-template-rows:subgrid;row-gap:0}
.tile > .spec .spec__row{align-content:start}
.tile__panel{
  position:relative;overflow:hidden;border-radius:var(--radius);
  /* Named so the feature tile's display type can size against this box rather
     than against the window. inline-size, not size: a container query on the
     block axis would make the panel's own height depend on its contents. */
  container:panel / inline-size;
  aspect-ratio:3/4;display:grid;place-items:center;
  background:var(--panel);
  text-decoration:none;
}
/* The splash behind the can.

   One of two cut-out backdrops, turned a different amount per product, so a row
   of tiles does not read as the same photograph printed six times. The turn is
   a hue-rotate on a full-spectrum image: it moves which colour sits where
   rather than tinting the whole thing one shade, which is what keeps the
   splashes looking like different pours instead of the same pour recoloured.

   ::before, because ::after is the hover wash. It stays behind both the giant
   faint product name and the can — this is a ground, and a ground that competes
   with the product it is behind has stopped being a ground. */
.tile__panel::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background-image:var(--splash,var(--splash-a));
  background-repeat:no-repeat;background-position:center;background-size:118%;
  /* Saturate and contrast, not just more opacity. Half-transparent over a pale
     blue gradient the paint went chalky — the spatter turned to pastel and the
     splash read as a watermark of itself. Opacity carries most of it back;
     the two filters put the pigment back into what is still a translucent
     layer, so the deep reds and blues in the artwork stay deep. */
  filter:hue-rotate(var(--splash-hue,0deg)) saturate(1.4) contrast(1.15);
  opacity:.82;
  transition:transform 460ms var(--ease);
}
.tile__art{
  width:72%;height:auto;
  filter:drop-shadow(0 16px 22px rgba(var(--shadow-rgb),.38));
  transition:transform 260ms var(--ease);
}
.tile__name{
  font-family:var(--font-display);font-weight:var(--fw-semibold);font-size:16px;letter-spacing:.01em;
  line-height:1.15;text-transform:uppercase;margin:0;padding-top:12px;color:var(--paper);
}
.tile__desc{
  font-family:var(--font-sans);font-size:var(--fs-micro);line-height:1.5;letter-spacing:.01em;
  /* Margin, not padding. overflow:hidden clips at the padding edge, so a
     padding-bottom let 8px of the fourth line bleed through under the clamp. */
  margin:6px 0 8px;padding:0;color:var(--ice-200);
  /* No clamp and so no ellipsis: concept.js cuts the blurb to its first
     sentence instead. The clamp was three lines and every blurb that ran past
     them ended in three dots — a row of five tiles all trailing off. A sentence
     that stops because it is finished is shorter *and* says more.

     The row track is still shared, so the tallest description in the band sets
     the height and every tile's spec block starts on the same line. */
  align-self:start;
}
.tile .spec{padding-bottom:9px}
.tile .marks{padding-bottom:10px;align-content:flex-start}

.tile__foot{
  display:flex;flex-direction:column;gap:8px;justify-content:flex-end;
  padding-top:9px;border-top:1px solid rgba(255,255,255,.18);
}
.tile__dots{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.tile__dot{
  display:inline-flex;align-items:center;gap:5px;
  font-family:var(--font-sans);font-size:10px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--ice-200);
}
.tile__dot i{
  width:8px;height:8px;border-radius:50%;flex:0 0 auto;
  box-shadow:inset 0 0 0 1px rgba(var(--shadow-rgb),.4);
}
.tile__dot--flat{color:var(--ice-300)}
.tile__buy{display:flex;align-items:center;justify-content:space-between;gap:var(--gap)}
.tile__price{
  font-family:var(--font-display);font-weight:var(--fw-semibold);font-size:20px;letter-spacing:var(--ls-snug);
  color:var(--paper);margin:0;display:flex;flex-direction:column;line-height:1.1;
}
.tile__price .micro{font-size:10px;font-weight:500}

/* The feature tile: two columns wide, product on the left, name and buy on the
   right, exactly the AURORA™ block in the reference. */
/* The feature tile keeps the reference's layout — name and buy inside the panel —
   and spans the whole six-row band, so the grid stays aligned around it. */
.tile--feature{grid-column:span 2;grid-row:span 9}
/* The splash stops at the halfway line on the feature tile. The right half of
   this panel is the only place on the page where body text sits on the panel
   itself, and a half-opacity splash of saturated paint under dark type takes
   the contrast wherever the paint happens to fall — legible over the pale
   spatter, not over the blue. Behind the can it can do what it likes. */
.tile--feature .tile__panel::before{right:52%;background-size:128%}
.tile--feature .tile__panel{
  grid-row:1 / -1;aspect-ratio:auto;height:100%;min-height:300px;
  /* The text half is the wider one. It was the narrower, and the longest
     product name in the range — DUCHESSE SATIN EXTERIOR — ran straight off the
     panel while the tin sat in a column half of which was empty. */
  grid-template-columns:.85fr 1.15fr;place-items:stretch;
  /* No longer a special case — every panel is this now. Which is the point:
     the feature tile used to be the one light block in a row of dark ones. */
  background:var(--panel);
}
.feature__art{
  align-self:center;justify-self:center;width:82%;height:auto;
  filter:drop-shadow(0 22px 32px rgba(var(--shadow-rgb),.42));
  transition:transform 260ms var(--ease);
}
/* The left padding is not zero any more. The column boundary falls at 47.5% and
   the splash behind the can runs to 52%, so text set flush to the column edge
   started on the last of the paint — and sat closer to the tin than to anything
   else on the card. A gap of its own gives the block a left edge to line up on. */
.feature__side{
  align-self:center;
  padding:clamp(14px,2vw,26px) clamp(14px,2vw,26px) clamp(14px,2vw,26px)
          clamp(18px,2.8vw,40px);
  display:flex;flex-direction:column;gap:12px;color:var(--ice-950);min-width:0;
}
.feature__desc{
  font-family:var(--font-sans);font-size:12px;line-height:1.55;letter-spacing:.01em;
  color:rgba(var(--ink-rgb),.78);max-width:34ch;
}
.feature__name{
  font-family:var(--font-display);font-weight:700;text-transform:uppercase;
  /* Sized against the panel, not the window. 3.4vw took its cue from the
     viewport, which says nothing about how much room this particular tile has —
     at 1300px it resolved to 44px in a 290px column and clipped an eight-letter
     name. cqi is one percent of the container's width, so the name now shrinks
     when its own column does. The vw value stays as the fallback for a browser
     without container queries. */
     40, not 42: at 42 the longest name in the range finished exactly on the
     padding edge, and fitting is not the same as clearing. */
  font-size:clamp(24px,3.2vw,40px);
  font-size:clamp(24px,13cqi,40px);
  line-height:.94;letter-spacing:-.01em;margin:0;
  color:var(--ice-950);
}
.feature__name sup{font-size:.32em;vertical-align:super;font-weight:500}
.feature__buy{display:flex;align-items:center;gap:12px}
/* Label above price. They were side by side with the octagon between them, and
   removing the octagon left "ADD TO CART" and "AED 121" touching with nothing
   in between — one string, unreadable. nowrap on the label stays: it broke
   across two lines before that. */
.feature__buy{flex-wrap:wrap}
.feature__meta{min-width:0;display:flex;flex-direction:column;gap:2px}
.feature__meta .micro{color:rgba(var(--ink-rgb),.72);white-space:nowrap}
.feature__price{
  font-family:var(--font-display);font-weight:600;font-size:clamp(18px,1.9vw,24px);
  margin:2px 0 0;color:var(--ice-950);
}
/* The wash sits behind the can here too, in the panel's own dark value. */
.feature__wash{
  position:absolute;left:4%;bottom:3%;margin:0;
  font-family:var(--font-display);font-weight:700;text-transform:uppercase;
  font-size:clamp(32px,4.1vw,64px);line-height:.86;letter-spacing:-.02em;
  color:rgba(var(--ink-rgb),.14);pointer-events:none;user-select:none;
}

/* ── Colour ──────────────────────────────────────────────────────────────── */
.colour{padding-top:clamp(26px,3vw,44px)}
.colour__head{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--gap);
  margin-bottom:clamp(14px,1.8vw,22px);flex-wrap:wrap}
.chips{display:grid;grid-template-columns:repeat(auto-fill,minmax(86px,1fr));gap:var(--gap)}
.chip{display:flex;flex-direction:column;gap:6px;min-width:0}
.chip__sw{
  aspect-ratio:1;border-radius:var(--radius);
  box-shadow:inset 0 0 0 1px rgba(var(--shadow-rgb),.28);
}
/* Name and code on their own lines, so both columns of text line up down the
   strip instead of ragging wherever the name happened to end. */
.chip__id,.chip__code{
  font-family:var(--font-sans);font-size:10px;letter-spacing:.1em;text-transform:uppercase;
  margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.chip__id{color:var(--paper)}
.chip__code{color:var(--ice-300)}
.disclaim{margin-top:clamp(18px,2.4vw,30px);max-width:70ch;letter-spacing:.06em;
  text-transform:none;line-height:1.7}

/* ── Foot ────────────────────────────────────────────────────────────────── */
.foot{
  margin-top:clamp(26px,3vw,44px);padding-top:16px;
  border-top:1px solid rgba(255,255,255,.22);
}
/* The row the footer has always been: mark, links, market. */
.foot__top{
  display:flex;align-items:center;justify-content:space-between;gap:var(--gap);
  flex-wrap:wrap;
}
.foot__links{display:flex;gap:clamp(14px,2vw,30px);flex-wrap:wrap}
.foot__links a{
  font:var(--fw-semibold) var(--fs-micro)/1.4 var(--font-sans);
  letter-spacing:.1em;text-transform:uppercase;color:var(--ice-100);
  text-decoration:none;border-bottom:1px solid rgba(var(--ink-rgb),.28);
  padding-bottom:2px;
}
.foot__links a:hover{color:var(--paper);border-bottom-color:currentColor}

/* Who runs the shop. A hairline above it and a measure on the paragraphs: this
   is the band people read once, when they are checking whether the company is
   real, and a line of legal text running the full width of a desktop screen is
   the hardest thing on the page to read.

   Same size as the rest of the footer. Shrinking a disclosure until it is
   technically present is the oldest trick in the trade. */
.foot__legal{
  margin-top:clamp(14px,1.8vw,22px);padding-top:clamp(12px,1.6vw,18px);
  border-top:1px solid rgba(255,255,255,.14);
  display:flex;flex-direction:column;gap:6px;
}
.foot__legal p{max-width:96ch;text-transform:none;letter-spacing:.02em;
  line-height:1.6}
.foot__legal strong{color:var(--ice-100);font-weight:var(--fw-semibold)}

/* Hover movement lives in the Motion section at the end of this file. */

/* ── Responsive ──────────────────────────────────────────────────────────── */
@media (max-width:1080px){
  /* The reel goes under the panel as a row — a third column this narrow turns
     the thumbnails into slivers. */
  .hero{
    grid-template-columns:minmax(0,1fr) minmax(0,1.1fr);
    grid-template-areas:"text panel" "reel reel";
  }
  .reel{flex-direction:row;align-items:center}
  /* The track is a grid now — flex-direction has no effect on it. Four across
     in one row, which is what the row layout wanted all along. */
  .reel__track{grid-template-columns:repeat(4,1fr)}
  .reel__count{flex:0 0 auto}
  .tiles{grid-template-columns:repeat(3,minmax(0,1fr))}
  .tile--feature{grid-column:span 3;grid-row:span 1}
  .tile--feature .tile__panel{grid-row:auto;min-height:260px}
}
@media (max-width:720px){
  .hero{grid-template-columns:minmax(0,1fr);grid-template-areas:"panel" "text" "reel";
    min-height:0}
  /* Back to 2x2 here. Four across in a 375px column came out at 54px a cell —
     nothing in a thumbnail that small is identifiable, which is the only reason
     a thumbnail exists. */
  /* align-items must be reset too. It is centre from the tablet rule, and in a
     column flex that stops the track from stretching — a grid with 1fr columns
     and no definite width resolved them to 0 and the cells vanished. */
  .reel{flex-direction:column;align-items:stretch}
  /* auto rows plus an aspect on the cell, not 1fr. Stacked in a column the track
     has no height of its own to divide, so `1fr` rows collapsed the cells to
     zero. Sizing them from their width is what works here. */
  .reel__track{grid-template-columns:1fr 1fr}
  .hero__panel{min-height:58vw}
  .bar__inner{flex-wrap:wrap;row-gap:12px}
  /* One row, never two. Six labels at the system's own micro size and tracking
     came to 406px against 343px of bar and broke onto a second line, which put
     four items on one row and two on the next — a nav that reads as two nav.

     Closing the gap to 8px and easing the tracking from .16em to .04em brings
     it to 323px, so all six sit in one row with the type at the size the system
     specifies — and with room to spare on a 360px phone, not just on the 375px
     one this was measured against. Narrower than that it scrolls sideways
     rather than wrapping: on a 320px screen there is no honest way to show six
     tracked uppercase words at once, and a row that runs off the edge at least
     says so.

     flex:0 0 auto on the links, or they would shrink to fit and hyphenate
     instead of overflowing. */
  .bar__nav{
    order:3;flex-basis:100%;justify-content:flex-start;
    flex-wrap:nowrap;gap:8px;
    overflow-x:auto;overscroll-behavior-x:contain;
    scrollbar-width:none;-ms-overflow-style:none;
  }
  .bar__nav::-webkit-scrollbar{display:none}
  .bar__nav a{flex:0 0 auto;white-space:nowrap;letter-spacing:.04em}
  .bar__utils{margin-inline-start:auto}
  .tiles{grid-template-columns:repeat(2,minmax(0,1fr))}
  .tile--feature{grid-column:span 2;grid-row:span 1}
  .tile--feature .tile__panel{grid-row:auto;grid-template-columns:1fr;min-height:0}
  /* Two narrow columns cannot carry a 7.4em label column beside its value. */
  :root{--label-w:5.8em}
  .spec dt{font-size:9px;letter-spacing:.08em}
  .tile .marks{display:none}   /* the marks need width to read as marks */
  .feature__side{padding:0 clamp(14px,4vw,22px) clamp(18px,4vw,24px)}
  .feature__art{width:62%;padding-top:18px}
  .feature__wash{display:none}
  .range__meta{gap:24px}
  .chips{grid-template-columns:repeat(auto-fill,minmax(68px,1fr))}
}

@media (prefers-reduced-motion:reduce){
  *{transition-duration:1ms !important}
  .tile:hover .tile__art,
  .tile--feature:hover .feature__art{transform:none}
}

/* ── Motion ───────────────────────────────────────────────────────────────
   Three separate jobs, and it matters which is which:

   1. The hero entrance runs once, on load, as a pure CSS animation. No JS is
      involved on purpose — an animation that hides its own element must not
      depend on a script to un-hide it. `both` holds the from-state through the
      delay and the to-state after, so whatever happens the hero ends visible.
   2. Blocks below the fold fade up as they arrive, armed by JS. That one *is*
      allowed to depend on JS, because JS is also what hides them.
   3. Switching the hero product is an interaction, not an entrance, so it is a
      transition — it can be triggered faster than it plays and has to retarget
      rather than restart.

   Everything animates transform and opacity only. Entrances use the page's
   ease-out; nothing bounces and nothing scales in from nothing. */
:root{
  --dur-in:520ms;      /* once-only entrance, so it can breathe past 300ms */
  --dur-reveal:420ms;
  --dur-swap-out:140ms;
  --dur-swap-in:260ms;
  --stagger:60ms;
}

@keyframes rise{ from{opacity:0;transform:translateY(14px)} }
@keyframes line-in{ from{transform:translateY(105%)} }
@keyframes fade{ from{opacity:0} }
@keyframes panel-in{ from{opacity:0;transform:translateY(18px)} }
@keyframes slide-left{ from{opacity:0;transform:translateX(22px)} }

/* ── 1. Hero entrance ────────────────────────────────────────────────────── */
.hero__panel{animation:panel-in var(--dur-in) var(--ease) both}
.hero__art{animation:rise var(--dur-in) var(--ease) both;animation-delay:90ms}
.hero__wash{animation:fade 900ms var(--ease) both;animation-delay:260ms}

/* The text column comes in line by line rather than as one slab.
   Scoped to .is-first, and that class is dropped once the entrance is over.
   Without the scope, switching product replayed the whole cascade: paintHero
   rebuilds the spec block and the marks, so those fresh elements picked up the
   entrance again — delays and all — and vanished for up to half a second on
   every click. A product switch is a 260ms crossfade, not an entrance. */
.hero.is-first .hero__text > *{animation:rise var(--dur-in) var(--ease) both}
.hero.is-first .hero__text > *:nth-child(1){animation-delay:60ms}
.hero.is-first .hero__text > *:nth-child(2){animation:none}   /* the name masks its own lines */
.hero.is-first .hero__text > *:nth-child(3){animation-delay:280ms}
.hero.is-first .hero__text > *:nth-child(4){animation-delay:320ms}
.hero.is-first .hero__text > *:nth-child(5){animation-delay:360ms}
.hero.is-first .hero__text > *:nth-child(6){animation-delay:400ms}
.hero.is-first .hero__text > *:nth-child(7){animation-delay:440ms}
.hero.is-first .hero__text > *:nth-child(8){animation-delay:480ms}

/* The headline slides up out of a mask — the one place on the page where the
   text arrives from somewhere rather than just appearing. Each line needs its
   own clipping box, which is why the mask is a wrapper and not overflow on the
   heading: one box would clip all the lines against the same edge. */
.display__mask{display:block;overflow:hidden}
.display__mask .display__line{
  animation:line-in var(--dur-in) var(--ease) both;
}
.display__mask:nth-child(1) .display__line{animation-delay:120ms}
.display__mask:nth-child(2) .display__line{animation-delay:190ms}
.display__mask:nth-child(3) .display__line{animation-delay:260ms}

.reel__item{animation:slide-left var(--dur-in) var(--ease) both}
.reel__item:nth-child(1){animation-delay:340ms}
.reel__item:nth-child(2){animation-delay:400ms}
.reel__item:nth-child(3){animation-delay:460ms}
.reel__item:nth-child(4){animation-delay:520ms}
.reel__count{animation:fade var(--dur-in) var(--ease) both;animation-delay:600ms}

/* ── 2. Scroll reveal ────────────────────────────────────────────────────── */
.js-reveal [data-reveal]{
  opacity:0;transform:translateY(16px);
  transition:opacity var(--dur-reveal) var(--ease),transform var(--dur-reveal) var(--ease);
  transition-delay:var(--reveal-delay,0ms);
}
.js-reveal [data-reveal="in"]{opacity:1;transform:none}

/* ── 3. Swapping the hero product ────────────────────────────────────────── */
/* A transition, not a keyframe: click two thumbnails quickly and this has to
   pick up from wherever it is instead of snapping back to the start. */
.hero__text,.hero__art{
  transition:opacity var(--dur-swap-in) var(--ease),transform var(--dur-swap-in) var(--ease);
}
.hero.is-swapping .hero__text,
.hero.is-swapping .hero__art{
  opacity:0;transform:translateY(8px);
  transition-duration:var(--dur-swap-out);
}
/* While swapping, the once-only entrance animations must not fight the
   transition — they are done by then anyway, but a rebuilt element would
   otherwise replay its delay and flash. */
.hero.is-swapping .hero__text > *{animation:none}

/* ── Press feedback ──────────────────────────────────────────────────────── */
/* Everything pressable acknowledges the press. The scale is slight on purpose;
   these are small controls and a big one reads as a bounce. */
.opt,.pill,.disc,.reel__item,.chip{
  transition:transform 140ms var(--ease),
             color var(--dur) var(--ease),
             background-color var(--dur) var(--ease),
             border-color var(--dur) var(--ease),
             opacity var(--dur) var(--ease);
}
.opt:active,.reel__item:active{transform:scale(.97)}

/* ── Hover ───────────────────────────────────────────────────────────────
   Gated: a touch screen latches :hover after a tap, and a card left mid-lift
   reads as broken rather than as interactive. */
@media (hover:hover) and (pointer:fine){
  .tile:hover .tile__art,
  .tile--feature:hover .feature__art{transform:translateY(-6px) scale(1.03)}
  .chip:hover .chip__sw{transform:translateY(-3px)}
  .mark{transition:border-color var(--dur) var(--ease)}
  .mark:hover{border-color:rgba(255,255,255,.62)}
}
.chip__sw{transition:transform 260ms var(--ease)}

@media (prefers-reduced-motion:reduce){
  /* Reduced motion means less movement, not less information: the fades stay,
     the travel goes, and nothing is left waiting on an animation. */
  .hero__panel,.hero__art,.hero__wash,.hero.is-first .hero__text > *,
  .display__mask .display__line,.reel__item,.reel__count{
    animation:fade 200ms linear both;animation-delay:0ms !important;
  }
  .js-reveal [data-reveal]{transform:none}
  .hero.is-swapping .hero__text,.hero.is-swapping .hero__art{transform:none}
  .tile:hover .tile__art,.tile--feature:hover .feature__art,
  .chip:hover .chip__sw{transform:none}
}

/* Hovering a panel used to wash it over with a library shade — a flat colour at
   82% across the whole card, plus a white plate along the bottom carrying that
   shade's name. Both are gone.

   Two reasons, and the second is the one that matters. The white plate was
   written as `.tile__panel:hover::before`, and ::before is now the splash: the
   `background` shorthand in it wiped the splash's image out and replaced it
   with a white-to-transparent gradient over the whole panel, so hovering a
   product fogged it. And even fixed, a flat 82% film over the splash undoes
   exactly what the splash is there for.

   The tinting demonstration is worth having and can come back — as something
   that happens beside the product rather than over it. */

/* The can has to sit above the splash — otherwise the paint covers the product.
   .feature__wash is left alone: it is already absolutely positioned, and giving
   it position:relative put it back into the panel's grid flow as a third item,
   which pushed the feature tile's side column onto a second row. A z-index is
   all a positioned element needs to come forward. */
.tile__art,.feature__art,.feature__side{position:relative;z-index:1}
.feature__wash{z-index:1}

/* What hover does now: the can lifts (motion section) and the splash behind it
   opens out a little. Nothing is laid over the panel. */
@media (hover:hover) and (pointer:fine){
  .tile:hover .tile__panel::before{transform:scale(1.07)}
}
@media (prefers-reduced-motion:reduce){
  .tile:hover .tile__panel::before{transform:none}
}

/* ── Category grid (home) ─────────────────────────────────────────────────
   The same panel and the same 4/5 proportion as everything else on the page. */
/* auto-fit, not auto-fill. The grid was leaving an eighth track open for a
   category that no longer has a tile, so the row of seven stopped short of the
   right edge with a tile's worth of empty field after it. auto-fit collapses
   the track that has nothing in it and the seven share the full width. */
.cats{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:var(--gap)}
.cat{display:flex;flex-direction:column;text-decoration:none;color:var(--paper);min-width:0}
.cat__panel{
  position:relative;overflow:hidden;border-radius:var(--radius);aspect-ratio:4/5;
  display:grid;place-items:center;background:var(--panel);
}
.cat__art{
  position:absolute;inset:0;width:100%;height:100%;object-fit:contain;padding:12% 16%;
  filter:drop-shadow(0 14px 20px rgba(var(--shadow-rgb),.34));
  transition:transform 260ms var(--ease);
}
.cat__none{display:block;width:34%;aspect-ratio:1;border-radius:var(--radius);
  background:rgba(var(--ink-rgb),.12)}
.cat__body{display:flex;flex-direction:column;gap:4px;padding-top:11px}
.cat__name{font:var(--fw-semibold) 16px/1.2 var(--font-display);letter-spacing:.01em;
  text-transform:uppercase;color:var(--paper)}
@media (hover:hover) and (pointer:fine){
  .cat:hover .cat__art{transform:translateY(-5px) scale(1.03)}
}

/* Products with no photograph — the colorants and the application tools. The
   panel says so rather than showing an empty box. */
.tile__none{
  font:var(--fw-semibold) 13px/1 var(--font-sans);letter-spacing:.16em;
  text-transform:uppercase;color:rgba(var(--ink-rgb),.45);
}

/* ── Brand pillars (home) ─────────────────────────────────────────────────
   Each one is a claim the catalogue actually makes — the seal or the figure it
   comes from is printed underneath it. */
/* Six across, explicitly. auto-fit kept landing on five and leaving the sixth
   alone on a second row, which reads as one card that did not fit rather than
   as a set. */
.pillars{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:var(--gap)}
@media (max-width:1400px){ .pillars{grid-template-columns:repeat(3,minmax(0,1fr))} }
@media (max-width:820px){  .pillars{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (max-width:520px){  .pillars{grid-template-columns:minmax(0,1fr)} }
/* A raised step of the brand ramp, not a white veil.

   These were rgba(255,255,255,.05) — five per cent of white over a deep blue is
   very nearly nothing, so the cards read as floating text with a hairline round
   it. A translucent blue-700 sits lighter than every part of the ground beneath
   them (800 at the top of the page through 950 at the foot), so the card reads
   as a surface wherever it lands. */
/* One colour each, from the same spectrum as the wave in the header — the two
   are the same campaign motif, so they should not be two different rainbows.

   The hue is laid over the blue surface rather than replacing it: six saturated
   blocks would shout down everything else on the page, and the text on them has
   to stay readable. At these opacities every card still measures over 9:1. */
.pillar{
  display:flex;flex-direction:column;gap:8px;padding:18px;
  border-radius:var(--radius);
  border:1px solid rgba(var(--hue),.55);
  background:
    linear-gradient(180deg, rgba(var(--hue),.34), rgba(var(--hue),.14)),
    rgba(21,70,139,.62);
}
.pillar:nth-child(1){--hue:229,46,139}   /* magenta */
.pillar:nth-child(2){--hue:240,89,43}    /* orange  */
.pillar:nth-child(3){--hue:245,179,27}   /* amber   */
.pillar:nth-child(4){--hue:87,185,71}    /* green   */
.pillar:nth-child(5){--hue:22,169,201}   /* cyan    */
.pillar:nth-child(6){--hue:140,75,201}   /* violet  */
.pillar svg{color:rgb(var(--hue))}
.pillar svg{width:22px;height:22px;stroke-width:1.6}
.pillar h3{font:var(--fw-semibold) 17px/1.25 var(--font-display);letter-spacing:.01em;
  margin:0;color:var(--paper)}
.pillar p{margin:0;font-size:var(--fs-micro);line-height:1.6;color:var(--ice-200)}
.pillar .src{
  margin-top:auto;padding-top:6px;font-size:9px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--np-blue-300);opacity:.85;
}

/* ── Two audiences ───────────────────────────────────────────────────────── */
.split{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:var(--gap)}
.split__half{
  display:flex;flex-direction:column;gap:10px;padding:clamp(18px,2vw,26px);
  border-radius:var(--radius);
  border:1px solid rgba(143,180,226,.28);
  background:rgba(21,70,139,.62);
}
.split__half h3{font:var(--fw-bold) clamp(20px,2.2vw,28px)/1.15 var(--font-display);
  letter-spacing:var(--ls-snug);text-transform:uppercase;margin:0;color:var(--paper)}
.split__half p{margin:0;font-size:var(--fs-caption);line-height:1.6;color:var(--ice-200)}
.ticks{list-style:none;margin:4px 0 0;padding:0;display:flex;flex-direction:column;gap:7px}
.ticks li{
  position:relative;padding-inline-start:20px;font-size:var(--fs-micro);line-height:1.55;
  color:var(--ice-100);
}
.ticks li::before{
  content:"";position:absolute;left:0;top:.45em;width:9px;height:9px;
  border-radius:50%;border:1px solid var(--np-blue-300);
}

/* ── Section head shared by the generated pages ──────────────────────────── */
/* The section head: words left, the panorama right.

   Every page carries it except the home page, which opens on the campaign
   lettering and does not need a second picture arguing with it — that is why
   the artwork hangs off .pagehead rather than off a shared wrapper.

   The image is 2.95:1 with its empty margin cut away (the build trims it), so
   it wants width rather than height, and align-self:center puts it against the
   middle of the heading block instead of stretching it to the tallest thing in
   the row.

   It takes about 45% of the row and bleeds off the right edge of the page. Both
   are deliberate. Words still lead — the heading and the lead keep the wider
   column and the reading order — but a decorative band asked to share a row
   with a display heading has to be big enough to be a picture rather than a
   stamp, and running it into the margin is what stops it looking like one more
   boxed-in element. The bleed is on the right only, so the text edge of the row
   stays on the page grid.

   aria-hidden and an empty alt, in the markup. It is Fuji, a pagoda, the Burj
   and the Frame in a wash of paint — it says "Japan and the Emirates", which
   the page has already said in words, and reading a list of landmarks out to
   somebody who cannot see it adds nothing. */
.pagehead{
  padding-block:clamp(18px,2.6vw,32px) clamp(14px,1.8vw,20px);
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,.82fr);
  gap:clamp(16px,2.4vw,40px);align-items:center;
}
.pagehead__text{min-width:0}
.pagehead__art{min-width:0;margin-inline-end:calc(var(--pad) * -1)}
.pagehead__art img{width:100%;height:auto;display:block}
@media (max-width:900px){
  /* Stacked, and the picture goes last: on a phone the heading and the lead are
     what the visitor came for, and a decorative band above them is a scroll. */
  .pagehead{grid-template-columns:minmax(0,1fr)}
  .pagehead__art{margin-inline:calc(var(--pad) * -1)}
}
.pagehead .display{max-width:14ch}
/* Justified, like the brand block. Both are the same measure and the same
   role: the paragraph that carries the page.

   hyphens:auto comes with it and is not optional. Justification without it
   pushes the slack into the word spaces, and at this measure a line with two
   long words in it opens gaps you can read down the column. The browser needs
   the language to hyphenate, which every page states on <html>. */
.pagehead__lead{
  margin:13px 0 0;max-width:60ch;font-size:var(--fs-caption);line-height:1.55;
  color:var(--ice-100);
  text-align:justify;hyphens:auto;
}
/* The wide variant sits under a heading at full width and is short — one or two
   lines, where justification has nothing to distribute and only risks a stretched
   last-but-one line. */
.pagehead__lead--wide{text-align:start;hyphens:manual}
.pagehead__row{display:flex;align-items:flex-end;gap:var(--gap);flex-wrap:wrap;
  margin-top:clamp(14px,1.8vw,20px)}
.pagehead__row .spec{flex:0 1 auto;min-width:200px}

/* The product name behind the can, on every tile — the device the feature tile
   already used. Deliberately left *under* the hover wash: when the panel fills
   with paint the name goes under it, which is what paint does to a wall. */
.tile__wash{
  position:absolute;left:5%;bottom:3%;margin:0;
  font-family:var(--font-display);font-weight:var(--fw-bold);text-transform:uppercase;
  font-size:clamp(22px,2.8vw,42px);line-height:.86;letter-spacing:var(--ls-tight);
  color:rgba(var(--ink-rgb),.15);pointer-events:none;user-select:none;
}
/* One word per line. Side by side, a two-word name ran straight off the panel
   and the second word was never seen; stacked, both read and the block still
   sits in the corner as texture. */
.wash__line{display:block;white-space:nowrap}
/* The feature panel is wider, so its own wash keeps its own size. */
.tile--feature .tile__wash{display:none}

/* ── Colour page ─────────────────────────────────────────────────────────── */
.filters{display:flex;flex-wrap:wrap;gap:var(--gap-chip,8px);margin-bottom:clamp(18px,2.4vw,28px)}
.filters .pill[aria-pressed="true"]{background:var(--paper);color:var(--np-blue-900)}
/* The whole library is 119 chips, so they run smaller than the teaser strip's. */
.chips--all{grid-template-columns:repeat(auto-fill,minmax(96px,1fr))}
/* On the colour page a chip is a button. Strip the UA's own button skin — the
   chip is already a complete object and a border round it would double the
   swatch's own inset line — and put the focus ring back, because removing an
   outline from something you can tab to is how a page stops being usable by
   keyboard. */
.chip--pick{
  border:0;background:none;padding:0;font:inherit;color:inherit;
  text-align:start;cursor:pointer;
}
.chip--pick:focus-visible{outline:2px solid var(--paper);outline-offset:3px}

/* The theme button. Same disc as its neighbours; only the icon changes, and
   concept.js draws it so it always matches what pressing it will do. */
.disc--theme svg{fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}

/* ── Language switcher ────────────────────────────────────────────────────
   Three flags. A flag is what people scan a header for, and it is read as
   "this market" as much as "this language" — which is why the Arabic one is the
   Emirates' and not a pan-Arab flag.

   The language's own name in its own script is in the markup beside each flag
   and hidden from sight, not from screen readers: "🇦🇪" announced on its own is
   "flag of the United Arab Emirates", which is not what the link does. */
.lang{display:flex;align-items:center;gap:2px;margin-inline-end:4px}
.lang__f{
  display:grid;place-items:center;width:30px;height:30px;
  border-radius:var(--radius);text-decoration:none;line-height:1;
  transition:background-color var(--dur) var(--ease),
             box-shadow var(--dur) var(--ease);
}
.lang__f svg{width:20px;height:14px;display:block;border-radius:1px;
  box-shadow:inset 0 0 0 1px rgba(var(--shadow-rgb),.25)}
.lang__f:hover{background:var(--np-blue-100)}
/* All three flags in full colour — greying the other two out made the switcher
   look half-disabled, as if two of the languages were not available.

   So the current one is marked the way every other chosen thing on this page is
   marked: the emblem's red under it. That red is the one accent the design
   system allows, and only ever for exactly this — a choice that has been made. */
.lang__f[aria-current]{box-shadow:inset 0 -2px 0 var(--accent)}
.lang__n{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
@media (max-width:720px){
  /* In the collapsed bar the flags sit with the search and basket discs. */
  .lang{order:2;margin-inline-end:0}
  .lang__f{width:28px;height:28px;font-size:16px}
}

/* ── Arabic: the text, not the page ───────────────────────────────────────
   The document stays dir="ltr" in every language. That is a decision, not an
   oversight: the client asked for the page not to be mirrored, only the text.

   So direction is applied here, to the elements that hold sentences, and never
   to the containers that hold layout. Arabic then sets right to left with its
   punctuation where it belongs, while the logo, the navigation, the grids and
   the artwork stay exactly where they are in the other two languages.

   Worth knowing if this is revisited: mirroring the whole interface is the
   convention for Arabic, and a reviewer expecting it will notice. The rest of
   the stylesheet was converted to logical properties on the way here, so
   dropping dir="rtl" back onto <html> would flip the layout correctly if that
   is ever wanted. */
html[lang="ar"] :is(h1,h2,h3,h4,p,li,dt,dd,td,th,figcaption,label,
                    .micro,.display,.tile__name,.pickp__name,.bline__name){
  direction:rtl;text-align:right;
}
/* Anything set inside a cell that is already aligned by the layout keeps its
   alignment — only the reading direction changes. */
html[lang="ar"] .sheet__sum,html[lang="ar"] .bline__sum,
html[lang="ar"] .pick__price,html[lang="ar"] .drawer__total{text-align:end}

/* A stock code is a Latin string of digits and dashes. Dropped into an Arabic
   run the bidi algorithm reorders the dashes around it; isolating it keeps the
   code exactly as it is printed on the tin. The same goes for prices and pack
   sizes, which are Latin sitting inside Arabic sentences. */
html[lang="ar"] .mono{direction:ltr;unicode-bidi:isolate;text-align:left}
html[lang="ar"] .pickp__packs .pill,html[lang="ar"] .bline__sum,
html[lang="ar"] .drawer__total,html[lang="ar"] .tile__price,
html[lang="ar"] .sheet__total,html[lang="ar"] .qnum{unicode-bidi:isolate}

/* ── Basket ───────────────────────────────────────────────────────────────
   The count on the bar's basket disc. Sits on the disc rather than beside it,
   so the bar's layout does not shift the moment something is added. */
.disc{position:relative}
.disc__n{
  position:absolute;top:-3px;right:-3px;min-width:18px;height:18px;padding:0 5px;
  display:grid;place-items:center;border-radius:9px;
  background:var(--accent);color:var(--np-white);
  font:var(--fw-semibold) 10px/1 var(--font-sans);letter-spacing:0;
}
.disc__n[hidden]{display:none}

/* The drawer is its own backdrop: the element covers the window and the panel
   sits inside it, which is what lets a click on the dark part close it without
   a second element to maintain. */
.drawer{
  position:fixed;inset:0;z-index:80;
  background:rgba(var(--ink-rgb),.55);
  display:flex;justify-content:flex-end;
  padding:clamp(10px,2vw,24px);
}
.drawer[hidden]{display:none}
/* The page behind must not scroll under an open drawer. */
.is-drawer,.is-drawer body{overflow:hidden}
.drawer__box{
  width:min(460px,100%);max-height:100%;overflow:auto;
  display:flex;flex-direction:column;gap:12px;
  padding:clamp(16px,2vw,24px);border-radius:var(--radius);
  background:var(--panel-text);
  color:var(--ink);
}
.drawer .micro--dim{color:rgba(var(--ink-rgb),.78)}
.drawer .mono{font-family:var(--font-mono);letter-spacing:.04em}
.drawer__head{display:flex;align-items:center;justify-content:space-between;gap:var(--gap)}
.drawer__close{color:var(--ink);border-color:rgba(var(--ink-rgb),.3)}
.drawer__close:hover{background:rgba(var(--ink-rgb),.08)}
.drawer__note{margin:0;text-transform:none;letter-spacing:.02em;line-height:1.6}

.blines{display:flex;flex-direction:column}
.bline{
  display:grid;
  grid-template-columns:44px minmax(0,1fr) auto auto auto;
  align-items:center;gap:10px;
  padding:12px 0;border-top:1px solid rgba(var(--ink-rgb),.18);
}
.bline:first-child{border-top:0}
.bline__shot{width:44px;height:44px;object-fit:contain}
.bline__id{min-width:0}
.bline__name{
  margin:0;font-family:var(--font-display);font-size:14px;font-weight:var(--fw-semibold);
  letter-spacing:var(--ls-snug);text-transform:uppercase;
}
.bline__id p{margin:2px 0 0}
/* Inline-block: the swatch is inside a line of text, and an inline box takes no
   width or height. */
.bline__sw{
  display:inline-block;width:9px;height:9px;border-radius:2px;
  box-shadow:inset 0 0 0 1px rgba(var(--shadow-rgb),.35);
}
.bline__qty{display:flex;align-items:center;gap:6px}
/* Typed as well as stepped. The native spinner is turned off — there are already
   two buttons either side of it, and a third pair inside the field is both ugly
   and, at this size, unhittable. */
.qnum{
  width:3.4em;height:26px;padding:0 4px;text-align:center;
  border:1px solid rgba(var(--ink-rgb),.3);border-radius:13px;
  background:transparent;color:var(--ink);
  font:var(--fw-semibold) 14px/1 var(--font-display);letter-spacing:var(--ls-snug);
  -moz-appearance:textfield;appearance:textfield;
}
.qnum::-webkit-outer-spin-button,
.qnum::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.qnum:focus-visible{outline:2px solid var(--np-blue-900);outline-offset:1px}
.qbtn{
  width:26px;height:26px;display:grid;place-items:center;padding:0;
  border:1px solid rgba(var(--ink-rgb),.3);border-radius:50%;
  background:transparent;color:var(--ink);cursor:pointer;font:inherit;line-height:1;
}
.qbtn:hover{background:rgba(var(--ink-rgb),.08)}
.bline__sum{
  margin:0;font-family:var(--font-display);font-weight:var(--fw-semibold);
  letter-spacing:var(--ls-snug);white-space:nowrap;
}
.bline__x{
  width:26px;height:26px;padding:0;border:0;background:transparent;cursor:pointer;
  color:rgba(var(--ink-rgb),.62);font-size:18px;line-height:1;
}
.bline__x:hover{color:var(--accent)}
.bline--gone{grid-template-columns:minmax(0,1fr) auto}
.drawer__foot{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--gap);
  padding-top:12px;border-top:1px solid rgba(var(--ink-rgb),.18);
}
.drawer__total{
  margin:0;font-family:var(--font-display);font-size:24px;
  font-weight:var(--fw-semibold);letter-spacing:var(--ls-snug);
}
@media (max-width:560px){
  /* Two rows: what it is, then what it costs. The stepper and the total will
     not fit beside a product name at this width.

     Every cell is placed explicitly. Left to auto-placement the remove button
     took a third row of its own and sat stranded under the stepper — it belongs
     at the end of the line it removes. */
  .bline{grid-template-columns:44px minmax(0,1fr) auto;row-gap:8px;align-items:start}
  .bline__shot{grid-column:1;grid-row:1}
  .bline__id{grid-column:2;grid-row:1}
  .bline__x{grid-column:3;grid-row:1;justify-self:end}
  .bline__qty{grid-column:2;grid-row:2}
  .bline__sum{grid-column:3;grid-row:2;justify-self:end;align-self:center}
}

/* ── Adam ─────────────────────────────────────────────────────────────────
   The shop's AI consultant, in this concept's clothes.

   The behaviour file is shared with the storefront — ../site/assets/js/adam.js,
   not a copy of it — but the storefront's skin is not: that one is a pill FAB
   on white cards, and this page is near-square corners on a deep blue field.
   Same consultant, same markup, different room. Every token below is the
   concept's own, so a change to the storefront's look cannot arrive here
   unannounced.

   z-index: above the page, below the basket drawer. A chat window over an open
   basket would be covering the thing it is helping with. */
.adam{
  position:fixed;right:clamp(12px,2vw,24px);bottom:clamp(12px,2vw,24px);
  z-index:70;font-family:var(--font-sans);
}
/* The one thing on the page that is not near-square. Everywhere else the radius
   is 3px because the reference has almost no rounding — but this is a floating
   button over the content rather than a part of the layout, and at 48px tall a
   3px corner reads as a clipped rectangle stuck to the corner of the window. A
   pill says "press me" and stays out of the grid's language. */
.adam-fab{
  display:inline-flex;align-items:center;gap:9px;height:48px;padding:0 20px 0 8px;
  border:1px solid var(--np-blue-100);border-radius:999px;cursor:pointer;
  background:var(--paper);color:var(--np-blue-900);
  box-shadow:0 14px 34px -18px rgba(var(--shadow-rgb),.9);
  font:var(--fw-semibold) var(--fs-micro)/1 var(--font-sans);letter-spacing:.1em;
  text-transform:uppercase;
  transition:transform 200ms var(--ease),background-color var(--dur) var(--ease);
}
.adam-fab:hover{background:var(--np-blue-100);transform:translateY(-2px)}
.adam-fab:active{transform:scale(.98)}
.adam-fab-text{white-space:nowrap}
.adam.is-open .adam-fab{opacity:0;pointer-events:none}

/* Round, to sit inside the pill without a square corner poking at its edge. */
.adam-face,.adam-avatar{
  flex:none;border-radius:50%;overflow:hidden;display:block;
  background:var(--np-blue-900);
}
.adam-face{width:34px;height:34px}
.adam-avatar{width:32px;height:32px}
.adam-face img,.adam-avatar img{width:100%;height:100%;object-fit:cover;display:block}

.adam-panel{
  position:absolute;right:0;bottom:0;
  width:min(380px,calc(100vw - 2 * clamp(12px,2vw,24px)));
  max-height:min(600px,calc(100vh - 120px));
  display:flex;flex-direction:column;overflow:hidden;
  /* The chat window rounds, and it is the only block on the page that does.
     Same reasoning as the button that opens it: this floats over the content
     instead of sitting in the grid, and at 380px wide a 3px corner reads as a
     rectangle cut out of the page rather than as a window laid on top of it. */
  border-radius:18px;
  background:var(--panel-text);
  color:var(--ink);box-shadow:0 26px 60px -28px rgba(var(--shadow-rgb),.95);
  /* A transition rather than a keyframe: the panel can be opened and closed
     faster than it animates, and a transition retargets from where it is. */
  opacity:0;visibility:hidden;transform:translateY(8px);
  transition:opacity 180ms var(--ease),transform 180ms var(--ease),visibility 180ms;
}
.adam-panel[data-open="true"]{
  opacity:1;visibility:visible;transform:none;
  transition:opacity var(--dur) var(--ease),transform var(--dur) var(--ease),visibility var(--dur);
}
.adam-head{
  display:flex;align-items:center;gap:10px;padding:11px 11px 11px 13px;
  border-bottom:1px solid rgba(var(--ink-rgb),.18);background:rgba(255,255,255,.4);
}
.adam-who{display:flex;flex-direction:column;line-height:1.2;min-width:0;flex:1}
.adam-who b{
  font-family:var(--font-display);font-size:14px;font-weight:var(--fw-semibold);
  letter-spacing:var(--ls-snug);text-transform:uppercase;
}
.adam-who small{font-size:10px;letter-spacing:.08em;text-transform:uppercase;
  color:rgba(var(--ink-rgb),.62)}
.adam-clear,.adam-x{
  width:30px;height:30px;flex:none;display:grid;place-items:center;
  border:0;border-radius:50%;background:none;color:rgba(var(--ink-rgb),.62);
  cursor:pointer;transition:background-color var(--dur) var(--ease),color var(--dur) var(--ease);
}
.adam-clear:hover,.adam-x:hover{background:rgba(var(--ink-rgb),.1);color:var(--np-blue-900)}

.adam-log{flex:1;overflow-y:auto;padding:14px 13px;display:flex;flex-direction:column;gap:9px}
.adam-msg{max-width:88%;font-size:13px;line-height:1.5;border-radius:14px}
.adam-msg p{margin:0 0 8px}
.adam-msg p:last-child{margin:0}
.adam-msg ul{margin:6px 0;padding-inline-start:18px}
.adam-msg li{margin:3px 0}
/* The corner nearest the speaker is tucked in. It is the whole reason a chat
   bubble reads as belonging to one side of the conversation. */
.adam-bot{align-self:flex-start;background:rgba(255,255,255,.62);padding:10px 12px;
  border-bottom-left-radius:5px}
.adam-me{align-self:flex-end;background:var(--np-blue-900);color:var(--paper);
  padding:10px 12px;border-bottom-right-radius:5px}
.adam-me a{color:inherit;text-decoration:underline;text-underline-offset:2px}
.adam-bot a{color:var(--np-blue-700);font-weight:var(--fw-semibold)}
.adam-msg.is-err{background:rgba(244,0,0,.1);color:var(--accent)}

/* A colour named in a reply reads as a chip, not as six characters of hex. */
.adam-sw{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.adam-sw i{
  width:14px;height:14px;border-radius:2px;flex:none;
  box-shadow:var(--shadow-inset-swatch);
}
.adam-sw .mono{font-family:var(--font-mono);font-size:11px;color:rgba(var(--ink-rgb),.62)}

.adam-dots{display:inline-flex;gap:4px;align-items:center;height:16px}
.adam-dots i{
  width:6px;height:6px;border-radius:50%;background:rgba(var(--ink-rgb),.5);
  animation:adam-dot 1.1s ease-in-out infinite;
}
.adam-dots i:nth-child(2){animation-delay:.16s}
.adam-dots i:nth-child(3){animation-delay:.32s}
@keyframes adam-dot{0%,60%,100%{opacity:.3}30%{opacity:1}}

.adam-starters{display:flex;flex-wrap:wrap;gap:6px;padding:0 13px 10px}
.adam-starters[hidden]{display:none}
.adam-starters button{
  padding:7px 13px;border-radius:999px;cursor:pointer;
  background:transparent;border:1px solid rgba(var(--ink-rgb),.3);color:var(--ink);
  font:var(--fw-semibold) 10px/1 var(--font-sans);letter-spacing:.06em;
  text-transform:uppercase;transition:background-color var(--dur) var(--ease);
}
.adam-starters button:hover{background:rgba(var(--ink-rgb),.08)}
/* The AI disclosure takes the whole row under the starter chips. */
.adam-note{
  flex-basis:100%;margin:2px 0 0;font-size:10px;line-height:1.5;
  color:rgba(var(--ink-rgb),.62);
}

.adam-form{
  display:flex;gap:8px;align-items:flex-end;padding:10px 11px;
  border-top:1px solid rgba(var(--ink-rgb),.18);
}
/* min-width:0 is required. A textarea has an intrinsic width of 20 columns and
   a flex item will not shrink below it, so the field grew past the panel and
   pushed the send button off the edge. */
.adam-input{
  flex:1;min-width:0;resize:none;max-height:120px;padding:10px 14px;
  /* Rounded, not a pill: the field grows to 120px as you type, and a pill that
     tall turns into a lozenge. */
  border-radius:16px;background:rgba(255,255,255,.55);
  border:1px solid rgba(var(--ink-rgb),.3);color:var(--ink);
  font:400 13px/1.4 var(--font-sans);
}
.adam-input:focus{outline:2px solid var(--np-blue-900);outline-offset:1px;border-color:transparent}
.adam-send{
  width:38px;height:38px;flex:none;border:0;border-radius:50%;cursor:pointer;
  background:var(--np-blue-900);color:var(--paper);display:grid;place-items:center;
  transition:background-color var(--dur) var(--ease);
}
.adam-send:hover{background:var(--np-blue-700)}

@media (max-width:720px){
  .adam-panel{
    position:fixed;right:clamp(12px,2vw,24px);left:clamp(12px,2vw,24px);bottom:70px;
    width:auto;max-height:min(78vh,calc(100vh - 140px));
  }
  .adam-msg{max-width:92%}
}
@media (prefers-reduced-motion:reduce){
  .adam-dots i{animation:none}
  .adam-fab:hover{transform:none}
}
/* The chat must not sit on top of an open basket. */
.is-drawer .adam{display:none}

/* ── Checkout ─────────────────────────────────────────────────────────────
   The details form and the order sheet, both inside the same drawer. */
.drawer__go{width:100%;justify-content:center;text-align:center}
.drawer__back{color:var(--ink);border-color:rgba(var(--ink-rgb),.3)}
.drawer__back:hover{background:rgba(var(--ink-rgb),.08)}

.cform{display:flex;flex-direction:column;gap:12px}
.cform__kind{margin-bottom:2px}
.cform__kind .pill{color:var(--ink);border-color:rgba(var(--ink-rgb),.3)}
.cform__kind .pill[aria-pressed="true"]{background:var(--np-blue-900);color:var(--paper);
  border-color:var(--np-blue-900)}
.fld{display:flex;flex-direction:column;gap:4px}
.fld input,.fld select,.fld textarea{
  width:100%;padding:9px 11px;border-radius:var(--radius);
  border:1px solid rgba(var(--ink-rgb),.3);background:rgba(255,255,255,.55);
  color:var(--ink);font:400 14px/1.4 var(--font-sans);
}
.fld textarea{resize:vertical}
.fld input:focus-visible,.fld select:focus-visible,.fld textarea:focus-visible{
  outline:2px solid var(--np-blue-900);outline-offset:1px;
}
/* The error is stated on the field, not in a summary at the top: a message that
   names the field but sits away from it makes you hunt for the field. */
.fld--bad input,.fld--bad select,.fld--bad textarea{border-color:var(--accent)}
.fld__err{color:var(--accent);text-transform:none;letter-spacing:.02em}
.cform__go{width:100%;justify-content:center;text-align:center;margin-top:4px}

.sheet{
  background:var(--np-white);color:var(--np-blue-950);
  padding:clamp(14px,2vw,20px);border-radius:var(--radius);
}
.sheet__t{width:100%;border-collapse:collapse;margin:10px 0}
.sheet__t td{
  padding:8px 6px;border-top:1px solid rgba(var(--ink-rgb),.18);
  font-size:13px;line-height:1.4;vertical-align:top;
}
.sheet__t td:first-child{width:2.4em;font-weight:600}
.sheet__t .mono{font-family:var(--font-mono);font-size:11px;letter-spacing:.02em}
.sheet__sum{text-align:end;white-space:nowrap;font-weight:600}
.sheet__total{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--gap);
  margin:0;padding-top:10px;border-top:2px solid var(--np-blue-950);
  font-family:var(--font-display);font-size:22px;font-weight:var(--fw-semibold);
  letter-spacing:var(--ls-snug);
}
.sheet__who{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));
  gap:6px 16px;margin:14px 0 0;padding-top:12px;
  border-top:1px solid rgba(var(--ink-rgb),.18);
}
.sheet__who dt{
  font:var(--fw-semibold) 9px/1.4 var(--font-sans);letter-spacing:.1em;
  color:rgba(var(--ink-rgb),.62);
}
.sheet__who dd{margin:0;font-size:13px;line-height:1.4}
.sheet__acts{display:flex;flex-wrap:wrap;gap:8px}
.sheet__acts .pill{color:var(--ink);border-color:rgba(var(--ink-rgb),.3)}
.sheet__acts .pill--solid{background:var(--np-blue-900);color:var(--paper);
  border-color:var(--np-blue-900)}

/* Printing the sheet. Everything but the drawer goes, the drawer stops being a
   fixed overlay, and the sheet prints on the paper's own white — the page's deep
   blue field would otherwise be a full-bleed ink flood on every copy. */
@media print{
  body > *:not(#basket){display:none !important}
  .drawer{position:static;display:block;background:none;padding:0}
  .drawer__box{
    width:auto;max-height:none;overflow:visible;padding:0;
    background:none;color:#000;
  }
  .sheet{padding:0}
  .noprint{display:none !important}
}

/* ── Choosing a paint for a shade ─────────────────────────────────────────
   Sits between the library's heading and the chips, so choosing a colour puts
   the next decision where the eye already is rather than at the bottom of a
   page of 1780 swatches. */
.pick{
  margin:clamp(14px,2vw,22px) 0 0;
  padding:clamp(16px,2vw,24px);
  border-radius:var(--radius);
  /* Not --panel. That gradient ends on blue-500, which is fine behind a black
     tin and one line of type and wrong under a panel that is nothing but text:
     the order line sits at the dark end, where the dim micro type measured
     2.9:1. Held in the top half of the same ramp it is 5.0:1 at its worst, and
     7.5:1 for everything set in ink. */
  background:var(--panel-text);
  color:var(--ink);
}
.pick[hidden]{display:none}
.pick__head{display:flex;align-items:center;gap:clamp(12px,1.6vw,20px);flex-wrap:wrap}
.pick__sw{
  width:56px;height:56px;flex:0 0 auto;border-radius:var(--radius);
  box-shadow:inset 0 0 0 1px rgba(var(--shadow-rgb),.28);
}
.pick__id{display:flex;flex-direction:column;gap:2px;min-width:0}
.pick__id b{font-family:var(--font-display);font-size:20px;letter-spacing:var(--ls-snug)}
/* The panel is light, so the dim micro type has to come off the ink, not off the
   ice ramp it uses on the deep field. .78 rather than .62: this type is 10px and
   tracked, and it has to clear 4.5:1 against the darkest end of the ground. */
.pick .micro--dim{color:rgba(var(--ink-rgb),.78)}
.pick__clear{margin-inline-start:auto;color:var(--ink);border-color:rgba(var(--ink-rgb),.3)}
.pick__clear:hover{background:rgba(var(--ink-rgb),.08)}
.pick__ask{margin:clamp(14px,1.8vw,20px) 0 8px}
.pick .filters .pill{color:var(--ink);border-color:rgba(var(--ink-rgb),.3)}
.pick .filters .pill[aria-pressed="true"]{background:var(--np-blue-900);color:var(--paper);
  border-color:var(--np-blue-900)}

.pickp__list{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));
  gap:var(--gap);margin-top:clamp(12px,1.6vw,18px);
}
/* Four rows on a subgrid — head, description, the manufacturer's line, packs —
   the same technique the product tiles use, and here for the same reason. NEON
   ELASTIC TEXTURE PLUS takes two lines where AQUATIC takes one, and with each
   card sizing its own rows that one long name pushed its description, its
   feature line and its pack buttons out of step with every card beside it. On a
   browser without subgrid the declaration is dropped and the cards go back to
   sizing themselves, which is untidy rather than broken. */
.pickp{
  grid-row:span 4;
  display:grid;grid-template-rows:subgrid;row-gap:6px;
  min-width:0;align-content:start;
}
/* The tin beside its name. Fixed box and object-fit:contain, because the shots
   are not all the same shape — a 20 kg pail is squarer than a 2.5 litre tin —
   and letting each one size itself would step the names down the row at
   different heights. min-width:0 on the text side or a long name stops the
   flex item from shrinking and pushes the card past its column. */
.pickp__head{display:flex;align-items:center;gap:10px;min-width:0}
.pickp__shot{
  width:54px;height:54px;flex:0 0 auto;object-fit:contain;object-position:center;
}
.pickp__id{min-width:0}
.pickp__name{
  margin:0;font-family:var(--font-display);font-size:15px;font-weight:var(--fw-semibold);
  letter-spacing:var(--ls-snug);text-transform:uppercase;
}
.pickp__meta{margin:0}
/* No clamp, and so no ellipsis. The text is cut to its first sentence in
   concept.js instead — a sentence that ends because it is finished reads as
   copy; two lines ending in three dots read as a fault. The cards stay level
   because they are on a subgrid: the row is as tall as its longest description
   and every card's pack row still starts on the same line. */
.pickp__desc{
  margin:2px 0 0;font-size:var(--fs-caption);line-height:1.5;
  color:rgba(var(--ink-rgb),.86);
}
/* The manufacturer's wording, behind a rule and set apart from ours. */
.pickp__feature{
  margin:6px 0 0;padding-top:6px;border-top:1px solid rgba(var(--ink-rgb),.18);
  color:rgba(var(--ink-rgb),.78);text-transform:none;letter-spacing:.02em;line-height:1.5;
}
/* A product with no catalogue line, or no description of ours, still gets the
   element so the subgrid keeps its rows — it just must not draw a rule under
   nothing. */
.pickp__desc:empty,.pickp__feature:empty{margin:0;padding:0;border:0}

/* The clamped text does not open on hover.

   It did, for one build: the full text was carried in data-full and drawn by a
   pseudo-element over the clamped paragraph. It worked, and it went — a panel
   that appears under the pointer while someone is reading down a row of five
   products is in the way more often than it is wanted, and the product page is
   one click away with all of it set properly.

   data-full stays on the elements. It costs nothing, it is what a tooltip or a
   tap-to-open would read, and it is the only place the untruncated string
   exists on this page. */

/* start, not stretch. The pack row is a subgrid track as tall as the card with
   the most sizes in the row, and a product with one pack had its single button
   stretched down the whole of it. */
.pickp__packs{
  display:flex;flex-wrap:wrap;gap:6px;margin-top:2px;
  align-items:flex-start;align-content:flex-start;
}
.pickp__packs .pill{color:var(--ink);border-color:rgba(var(--ink-rgb),.3)}
.pickp__packs .pill[aria-pressed="true"]{background:var(--np-blue-900);color:var(--paper);
  border-color:var(--np-blue-900)}

.pick__line{
  display:flex;align-items:center;gap:clamp(10px,1.4vw,18px);flex-wrap:wrap;
  margin-top:clamp(14px,1.8vw,20px);padding-top:clamp(12px,1.6vw,16px);
  border-top:1px solid rgba(var(--ink-rgb),.18);
}
.pick__line b{font-family:var(--font-display);letter-spacing:var(--ls-snug)}
.pick__price{
  margin-inline-start:auto;font-family:var(--font-display);font-size:20px;
  font-weight:var(--fw-semibold);letter-spacing:var(--ls-snug);
}
/* The stock code keeps the mono face for the same reason it does everywhere
   else: it is a machine string of digits and dashes, and a proportional face
   makes it materially harder to check against a can. */
.pick .mono{font-family:var(--font-mono);letter-spacing:.04em}
@media (max-width:600px){
  .pick__price{margin-inline-start:0}
}
.strip__more{margin:clamp(16px,2vw,24px) 0 0}

/* ── The brand block ──────────────────────────────────────────────────────
   Text left, the lettering right. The artwork is the campaign graphic, not the
   logo — the logo itself is the lockup in the bar and is never recoloured or
   collaged. This one is supplied cut out on transparency, so it sits straight
   on the deep field it was drawn for. */
/* The artwork column is the wider of the two and runs from the text straight
   off the right edge of the window.

   That is what hides the clip. The pour enters from the left and leaves to the
   right, and with a gap on either side you could see the exact line where the
   paint appeared out of nothing and the exact line where it stopped. Butted up
   against the text on one side and bled past the page padding on the other,
   there is no visible frame for it to start inside. */
.brandline{
  display:grid;grid-template-columns:minmax(0,.92fr) minmax(0,1.3fr);
  gap:clamp(8px,1.2vw,22px);align-items:center;
}
.brandline__text{display:flex;flex-direction:column;gap:10px;min-width:0}
.brandline__p{
  margin:0;max-width:60ch;font-size:var(--fs-caption);line-height:1.55;
  color:var(--ice-100);
  text-align:justify;hyphens:auto;
}
/* Bled by exactly the frame's own padding, so the artwork finishes flush with
   the window and not a pixel past it. */
.brandline__art{min-width:0;margin-inline-end:calc(var(--pad) * -1)}
.brandline__art img{width:100%;height:auto;display:block}

/* The pour: a clip that runs once and hands over to the still.

   Both come off their white ground in tools/build_brand_pour.py, so the artwork
   sits straight on the field with no plate around it — the same treatment the
   lettering has everywhere else on the site.

   The still is the clip's own last frame, so the two are the same picture at
   the same size and there is nothing to line up. It was a separately supplied
   file of the same artwork at 1376x768 and the swap jumped: rendered apart, two
   pictures do not land on the same geometry, and the eye catches a fraction of
   a percent when it happens in one step. object-fit:fill keeps them mapped onto
   each other if a re-encode ever changes the clip's dimensions; cover would
   crop the difference off one of them instead. */

/* The left edge is faded out, because that is where the frame shows.

   The pour enters from off-frame left, so for the first seconds a thick band of
   paint is sliced by a dead straight vertical line halfway across the page —
   the boundary of the video, sitting in open field with nothing to explain it.
   The right edge has no such problem: it runs off the window.

   The mask is on the container, not on the clip. Masking only the clip would
   fade the blossom roller away for six seconds and then snap it back the
   instant the still took over. */
.pourart{
  position:relative;margin:0;
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 11%);
  mask-image:linear-gradient(90deg,transparent 0,#000 11%);
}
.pourart__still{width:100%;height:auto;display:block}
.pourart__clip{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:fill;
  opacity:0;transition:opacity 320ms var(--ease);
}

/* The handover, and why it is one-sided.

   While the clip runs the still has to be out of the way — it is the finished
   lettering, and leaving it under a transparent clip would show the ending
   through the whole animation. At the end the still comes back instantly,
   underneath a clip frozen on the same frame, and only the clip fades. Nothing
   is ever half-transparent over half-transparent: cross-fading two cut-outs of
   the same artwork dips the letters to about three-quarters opacity in the
   middle and the field shows through them.

   The 320ms is not there to move anything — the picture underneath is the same
   frame and does not shift. It covers the one thing that does change: the clip
   is compressed for six seconds of motion and the still is that frame at full
   quality, so the fade is the artwork coming into focus. */
.pourart.is-running .pourart__still{opacity:0}
.pourart.is-running .pourart__clip{opacity:1;transition:none}

@media (max-width:900px){
  /* Stacked, and the lettering leads: at this width it is the thing that says
     which brand this is before a word is read. */
  .brandline{grid-template-columns:minmax(0,1fr)}
  /* Stacked, it bleeds both ways: the same reason, and edge to edge is the only
     width at which the pour has nowhere to visibly begin. */
  .brandline__art{order:-1;margin-inline:calc(var(--pad) * -1)}
}

/* ── Brand block: the ways in ─────────────────────────────────────────────
   The block opens the page now, so it carries what the hero used to: where the
   products are and where the colours are. */
.brandline__actions{display:flex;flex-wrap:wrap;gap:var(--gap-chip,8px);margin-top:6px}
.pill--solid{background:var(--paper);color:var(--np-blue-900);border-color:var(--paper)}
.pill--solid:hover{background:var(--np-blue-100);color:var(--np-blue-950)}

/* Columns, not a wrapped flow. Nine labels of nine different lengths flowing
   into three lines left a ragged block with nothing lining up under anything —
   readable, but it looked like text that had spilled rather than a list of
   places to go. On a grid every label starts on a column, so the eye can go
   down as well as across.

   auto-fit rather than auto-fill: with nine items and room for four columns the
   last row holds one, and auto-fill would keep three empty tracks open and
   stretch that single item across a quarter of the block. */
.brandline__cats{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));
  gap:7px 18px;margin-top:4px;
}
.brandline__cats a{
  font:var(--fw-semibold) var(--fs-micro)/1 var(--font-sans);letter-spacing:.1em;
  text-transform:uppercase;color:var(--ice-200);text-decoration:none;
  padding-bottom:3px;border-bottom:1px solid transparent;
  transition:color var(--dur) var(--ease),border-color var(--dur) var(--ease);
}
.brandline__cats a:hover{color:var(--paper);border-bottom-color:var(--accent)}

.brandline__colour{display:flex;flex-direction:column;gap:9px;margin-top:8px}
.swatchrow{display:flex;flex-wrap:wrap;gap:7px}
/* Square, near-flush, and each one carries its own name for a screen reader and
   a tooltip — a row of unlabelled colour blocks tells you nothing about what you
   are about to click. */
.swatchrow__sw{
  width:36px;height:36px;border-radius:var(--radius);display:block;
  box-shadow:var(--shadow-inset-swatch);
  transition:transform 200ms var(--ease);
}
.swatchrow__sw span{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
.brandline__all{color:var(--np-blue-300);text-decoration:none}
.brandline__all:hover{color:var(--paper)}
@media (hover:hover) and (pointer:fine){
  .swatchrow__sw:hover{transform:translateY(-3px)}
}

/* The block is the top of the page now, so it gets the entrance the hero had. */
.brandline.is-first .brandline__text > *{animation:rise var(--dur-in) var(--ease) both}
.brandline.is-first .brandline__text > *:nth-child(2){animation-delay:80ms}
.brandline.is-first .brandline__text > *:nth-child(3){animation-delay:140ms}
.brandline.is-first .brandline__text > *:nth-child(4){animation-delay:180ms}
.brandline.is-first .brandline__text > *:nth-child(5){animation-delay:220ms}
.brandline.is-first .brandline__text > *:nth-child(6){animation-delay:260ms}
.brandline.is-first .brandline__text > *:nth-child(7){animation-delay:300ms}
.brandline.is-first .brandline__text > *:nth-child(8){animation-delay:340ms}
.brandline.is-first .brandline__art{animation:panel-in var(--dur-in) var(--ease) both;
  animation-delay:120ms}
@media (prefers-reduced-motion:reduce){
  .brandline.is-first .brandline__text > *,.brandline.is-first .brandline__art{
    animation:fade 200ms linear both;animation-delay:0ms !important}
}

/* A lead sitting under a full-width heading row needs the width to match, or it
   reads as a leftover column beside empty space. */
.pagehead__lead--wide{max-width:82ch}

