/* ShopYourHue — site-wide styles: the storefront (the palette picker panel,
   category chips, product grid, detail pop-up), the shared header/footer and
   cookie bar injected by components.js, the static pages, and the seasons
   index and season pages (fan deck, fabric swatches). Phone-width overrides
   sit next to the rules they adjust. */

* {
  box-sizing: border-box;
}

/* The two typefaces, served from this site rather than from Google. Both are
   variable fonts, so one file covers every weight the site uses — and Fraunces
   also carries the optical-size axis the headings ask for with
   font-optical-sizing. Only the Latin characters are included; that is all the
   site sets. Each page asks for these files in its <head>, so they are on
   their way before this stylesheet has finished being read. */
@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 300 600;
  font-display: swap;
  src: url("/fonts/fraunces-latin.woff2") format("woff2");
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("/fonts/inter-latin.woff2") format("woff2");
}


/* Hold the scrollbar's slot open whether or not the page is long enough to
   need one. Without this, a short page (a season, Contact) is drawn a
   scrollbar's width wider than a long one (the store, the seasons index), so
   every navigation between the two slides the header nav, the footer and the
   whole grid sideways — the "jump" a shopper sees between pages. Browsers with
   overlay scrollbars reserve nothing and are unaffected. */
html {
  scrollbar-gutter: stable;
}

:root {
  --ink: #242424;
  --ink-soft: #585a5a;
  --ink-muted: #727272;
  --paper: #faf9f7;
  --card: #ffffff;
  --warm: #eae4d7;
  --line: #d3d3d3;
  --line-light: #f0f0f0;
  --picked: #f5f1e8; /* the ground of a ticked palette tile in the picker */
  --accent: #f1ba35; /* unused since the typography pass; nothing on the site is yellow */
  --radius: 8px;
  --font-body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-display: "Fraunces", Georgia, "Times New Roman", serif;
}

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--ink);
  background: var(--paper);
  line-height: 1.5;
}

/* components.js fills the header after the HTML is parsed. Hold its final
   height meanwhile, so a page never paints headerless and then drops 66px
   when the wordmark and nav arrive. 22px wordmark x 1.5 + 32px padding + 1px
   border; the mobile block below matches its 20px wordmark. */
#site-header {
  min-height: 66px;
}

.site-header {
  display: flex;
  align-items: center;
  /* Not space-between: the wordmark and the palette chip belong together on
     the left, and `.header-right` takes itself to the far edge with an auto
     margin. That way the chip appearing does not pull the nav inward. */
  justify-content: flex-start;
  gap: 12px;
  flex-wrap: wrap;
  padding: 16px 20px;
  /* The full hairline, not the pale one: this is the only bar the grid scrolls
     under, and white-on-off-white needs a visible edge or the cards appear to
     slide beneath nothing and simply stop. */
  border-bottom: 1px solid var(--line);
  background: var(--card);
  /* Stays on screen while the page scrolls, so leaving for Favorites doesn't
     mean scrolling all the way back up first — and so returning lands you
     where you actually were. On the storefront it carries the category tabs
     too, so this is the one and only thing that freezes. Below the detail
     overlay (50) and the bottom bar (60), above everything in the page. */
  position: sticky;
  top: 0;
  z-index: 30;
}

.site-title {
  margin: 0;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: 22px;
  font-weight: 500;
}

/* ---- Palette button (the way into the picker, until a palette is picked) --- */

/* A pill beside Filters, the same size and the same shape, because the two do
   the same job and a shopper reads them as a pair. It was a band across the
   whole bar, and the width was the whole trouble: a thousand pixels of header
   holding a hundred pixels of grey text, on the page's own background with one
   hairline above it, sitting a pixel clear of the small grey line about
   commission underneath. Two strips of small grey type running edge to edge,
   and neither one looked like a thing you could press. A button that looks like
   a button is found faster than a band that doesn't. It is always there: with
   no palette it says "Palette", and once one is chosen it names it — see
   app.js. */
.palette-chip {
  position: relative;
  display: none;
  flex: none;
  align-items: center;
  gap: 4px;
  order: 1;
  margin: 4px 0 0;
  padding: 5px 10px 5px 12px;
  font: inherit;
  font-size: 0.9rem;
  line-height: 1.2;
  color: var(--ink-soft);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 999px;
  white-space: nowrap;
  cursor: pointer;
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}

.palette-chip.visible {
  display: flex;
}

/* Same reach-for-it response as Filters, so the pair stay a pair. */
.palette-chip:hover {
  color: var(--ink);
  background: var(--paper);
}

.palette-chip:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* The chevron is the smaller half of the pair: it says a panel opens, and it
   shouldn't weigh as much as the word. */
.palette-chip-caret {
  width: 14px;
  height: 14px;
  flex: none;
  opacity: 0.7;
}

/* With no palette chosen the label is asking a question rather than naming a
   season, so it drops the display face and the underline and takes the size of
   the word on the button beside it. */
.palette-chip-label.prompt {
  font-family: var(--font-body);
  font-size: inherit;
  color: inherit;
  text-decoration: none;
}

/* ---- Once a palette is chosen ---- */

/* The pill stays, naming the palette, with a dot of its colors in front and
   the same caret after. It used to turn into the colored edge and nothing else,
   which said what was on but not that it could be changed: nobody presses a
   line. Now the pill is what says "you are shopping this, and you can change
   it", and the edge below is only the colors. */
.palette-chip.picked {
  display: flex;
  max-width: 200px;
  color: var(--ink);
  font-weight: 500;
}

.palette-chip-label {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A small copy of the tray's rings: the palette's colors in arcs round an
   empty middle, the hole cut by the same mask. --chip-dot comes from app.js. */
.palette-chip-swatch {
  display: none;
  flex: none;
  width: 16px;
  height: 16px;
  margin-right: 3px;
  border-radius: 50%;
  background: var(--chip-dot, var(--line));
  -webkit-mask: radial-gradient(circle, transparent 36%, #000 37%);
  mask: radial-gradient(circle, transparent 36%, #000 37%);
}

.palette-chip.picked .palette-chip-swatch {
  display: block;
}

/* ---- The palette itself, drawn as the header's bottom edge ----

   Painted with the chosen colors at full strength — nothing dropped, nothing
   deepened — so the line the whole grid scrolls under simply becomes those
   colors. Absolutely positioned, so it costs no row and nothing in the header
   moves when a palette is picked. The box is taller than the line it draws:
   15px of button, 8px of color. The extra 7px is the header's own bottom
   padding, empty below the tabs, so it is still big enough to press without
   reaching the tabs and stealing their clicks. */
.palette-edge {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 15px;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}

.palette-edge[hidden] {
  display: none;
}

/* The colors themselves: a line, not a band. At 8px they are plainly the
   palette and still plainly the bottom of the bar; thicker and five blocks of
   flat color across the whole screen stop being an edge and start being a
   banner. --chip-ring comes from app.js; the grey fallback is what shows in the
   moment before the color data lands. */
.palette-edge::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 8px;
  background: var(--chip-ring, var(--line));
}

/* ---- The picker ----

   The picker is the front page of the store for anyone without a palette, not
   a panel over it. It used to float over a dimmed grid, which put the clothes
   on screen before the one question that decides which clothes are hers — a
   store that sells by color opening on a rack of every color. Now a first
   visit opens on the picker, the way a big store opens on its front window.

   It is a front window, not a door: the store is right there underneath it,
   every color, with its tabs and Filters in the header, for anyone who would
   rather look around first. Nothing is hidden while the picker shows; the
   picker is simply the first thing on the page.

   Whether it shows is the `landing` class on <html>: set before the first
   frame by the script in index.html's <head>, and kept up to date by
   renderPaletteState in app.js. */
html:not(.landing) .palette-banner {
  display: none;
}

.palette-banner {
  /* A screen tall below the header, so the store starts just out of sight
     and the cue at the bottom (.hero-browse) sits on the bottom edge of the
     screen, pointing at it. The two halves inside (.hero) stretch to fill it.
     The header's height is measured by app.js (--header-h) because the tab
     row in it wraps differently on every width. */
  display: flex;
  flex-direction: column;
  min-height: calc(100vh - var(--header-h, 110px));
  min-height: calc(100svh - var(--header-h, 110px));
  background: var(--card);
  border-bottom: 1px solid var(--line);
  text-align: left;
}

.palette-banner:focus {
  outline: none;
}

/* Said to screen readers, not shown: the panel's h1, which names the whole
   thing while each half carries its own visible heading. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* What the site is, in a band across the top of the picker: the one centred
   thing on it, so it reads as the shop's opening line rather than as the
   title of a document, with the two halves below it as the two ways in. The
   headline is set larger and lighter than the halves' headings, so it reads
   as the page's voice rather than as a third heading. Under it, three short points in small
   letter-spaced type, the way a store's banner lists what it offers, instead
   of a sentence to read. Kept short enough that the picker below still fits
   on a laptop's first screen. */
.hero-intro {
  width: min(1240px, 100%);
  margin: 0 auto;
  padding: clamp(18px, 2vw, 24px) clamp(20px, 4vw, 52px) clamp(16px, 1.6vw, 20px);
  border-bottom: 1px solid var(--line);
  text-align: center;
}

.hero-intro-title {
  margin: 0 0 10px;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: clamp(28px, 3vw, 40px);
  font-weight: 350;
  line-height: 1.08;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

/* Three points in a row, split by small round marks rather than commas or
   pipes: the same round shape as the palette rings below. They wrap as whole
   points on a phone, never mid-point. */
.hero-intro-points {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 0;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.hero-intro-points li {
  display: flex;
  align-items: center;
  white-space: nowrap;
}

.hero-intro-points li + li::before {
  content: "";
  width: 4px;
  height: 4px;
  margin: 0 14px;
  border-radius: 50%;
  background: var(--ink-muted);
}

/* A phone has room for two of the points on a line, not three, and a wrapped
   point would start its new line with a stray mark. So the last one takes a
   line of its own, on purpose, without the mark. */
@media (max-width: 767px) {
  .hero-intro-points li:last-child {
    flex-basis: 100%;
    justify-content: center;
  }

  .hero-intro-points li:last-child::before {
    display: none;
  }
}

/* The two halves of the picker, side by side with a hairline between: on the
   left the way to find your season, on the right the way to shop the one you
   already know. Equal halves because they are equal answers to the one
   question the page asks — neither is the small print of the other. Held to a
   readable width on a wide screen, with the white running edge to edge. */
.hero {
  flex: 1;
  display: grid;
  grid-template-columns: 1fr 1fr;
  width: min(1240px, 100%);
  margin: 0 auto;
}

.hero-side {
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: clamp(28px, 4vw, 52px);
}

.hero-side + .hero-side {
  border-left: 1px solid var(--line);
}

.hero-eyebrow {
  margin: 0 0 10px;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.hero-heading {
  margin: 0 0 12px;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: clamp(30px, 3.2vw, 42px);
  font-weight: 400;
  line-height: 1.05;
  text-wrap: balance;
}

.hero-sub {
  max-width: 44ch;
  margin: 0 0 24px;
  line-height: 1.5;
  color: var(--ink-soft);
}

/* The main way in on the left: filled in ink, the one solid thing in the
   panel, because most people arriving don't know their season yet. */
.hero-cta {
  align-self: flex-start;
  padding: 14px 26px;
  border-radius: var(--radius);
  background: var(--ink);
  color: #fff;
  font-size: 15px;
  font-weight: 500;
  text-decoration: none;
  transition: opacity 0.15s;
}

.hero-cta:hover {
  opacity: 0.85;
}

.hero-how {
  margin: 10px 0 0;
  font-size: 13px;
  color: var(--ink-muted);
}

/* Each way in is a quiet link of its own, underlined so it reads as something
   to press, and darker than the words around it but never as loud as the
   button above. */
.hero-how a {
  color: var(--ink-soft);
  text-decoration: underline;
  text-decoration-color: var(--line);
  text-underline-offset: 3px;
  transition: color 0.15s, text-decoration-color 0.15s;
}

.hero-how a:hover {
  color: var(--ink);
  text-decoration-color: currentColor;
}

/* ---- The phone and its three steps ---- */

.hero-demo {
  display: grid;
  grid-template-columns: minmax(0, 168px) minmax(0, 1fr);
  gap: clamp(20px, 3vw, 40px);
  align-items: center;
  margin: 4px 0 28px;
}

.hero-phone {
  aspect-ratio: 9 / 18;
  padding: 5px;
  border-radius: 28px;
  background: #1c1c1c;
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.14);
}

.hero-screen {
  position: relative;
  height: 100%;
  overflow: hidden;
  border-radius: 23px;
  background: var(--card);
}

/* The three screens are stacked and cross-fade: only the one marked .on shows. */
.hero-scr {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.45s ease;
}

.hero-scr.on {
  opacity: 1;
}

.hero-cam img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 20%;
}

/* Camera framing marks and a shutter button, so the first screen reads as
   "taking a selfie" rather than just a photo of someone. */
.hero-corner {
  position: absolute;
  width: 16px;
  height: 16px;
  border: 0 solid rgba(255, 255, 255, 0.95);
}

.hero-corner.tl { top: 10px; left: 10px; border-top-width: 2px; border-left-width: 2px; }
.hero-corner.tr { top: 10px; right: 10px; border-top-width: 2px; border-right-width: 2px; }
.hero-corner.bl { bottom: 10px; left: 10px; border-bottom-width: 2px; border-left-width: 2px; }
.hero-corner.br { bottom: 10px; right: 10px; border-bottom-width: 2px; border-right-width: 2px; }

.hero-shutter {
  position: absolute;
  bottom: 12%;
  left: 50%;
  width: 30px;
  height: 30px;
  margin-left: -15px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.45);
}

.hero-result {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 14px;
  background: var(--paper);
  text-align: center;
}

.hero-result small {
  font-size: 10.5px;
  color: var(--ink-muted);
}

.hero-result strong {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 400;
  line-height: 1.1;
}

.hero-dots {
  display: flex;
  gap: 4px;
  margin: 6px 0;
}

.hero-dots span {
  width: 15px;
  height: 15px;
  border-radius: 50%;
}

/* The third screen is the store itself, shrunk to fit the phone: the same
   wordmark and tabs, the colored edge a chosen palette puts under the header,
   and a feed of cards built like the real ones — photo with its heart, name,
   price and category. A tidy grid of pictures read as a mood board; this reads
   as "and then you shop here", which is the step it is illustrating.
   Everything is measured in cqw (hundredths of the screen's width) instead of
   pixels, so the small phone on a mobile screen is the same picture, only
   smaller, rather than a crowded one. */
.hero-screen {
  container-type: size;
}

.hero-shop {
  display: flex;
  flex-direction: column;
  background: var(--paper);
}

.hero-shop-bar {
  position: relative;
  flex: none;
  padding: 11cqw 6cqw 0;
  background: var(--card);
  text-align: center;
}

.hero-shop-bar b {
  display: block;
  font-family: var(--font-display);
  font-size: 8.5cqw;
  font-weight: 500;
  line-height: 1;
}

.hero-shop-tabs {
  display: flex;
  justify-content: center;
  gap: 5cqw;
  padding: 4.5cqw 0 5cqw;
}

.hero-shop-tabs i {
  font-size: 4.6cqw;
  font-style: normal;
  color: var(--ink-soft);
}

.hero-shop-tabs i.on {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 0.4cqw;
  text-underline-offset: 1.2cqw;
}

/* The chosen palette's colors along the header's foot, as on the real
   header. Painted by app.js as a background; grey until the colors load. */
.hero-shop-edge {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1.6cqw;
  background: var(--line);
}

/* A window onto the feed. The feed is taller than it, and slides up inside it
   while this screen is showing, the way a thumb scrolls a shop. */
.hero-shop-view {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  container-type: size;
}

.hero-shop-feed {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 3.5cqw;
  padding: 4cqw 4cqw 6cqw;
}

.hero-shop.on .hero-shop-feed {
  animation: heroscroll var(--step-ms, 5.6s) ease-in-out both;
}

/* A beat to look at the top row, an unhurried scroll to the bottom, and a beat
   there before the phone starts over. 100cqh - 100% is exactly the distance
   from the feed's top resting place to its bottom one. */
@keyframes heroscroll {
  0%,
  22% {
    transform: translateY(0);
  }
  78%,
  100% {
    transform: translateY(calc(100cqh - 100%));
  }
}

.hero-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
  border: 1px solid var(--line-light);
  border-radius: 2.5cqw;
  background: var(--card);
}

.hero-card-img {
  position: relative;
  display: block;
  aspect-ratio: 3 / 4;
  background: #f4f1ec;
}

/* Cut-out garments standing on the bottom edge, all on one soft ground, so
   six photos from different shoots read as one store. */
.hero-card-img img {
  position: absolute; /* so a tall photo cannot stretch its 3:4 frame */
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: 50% 100%;
  padding: 8% 6% 0;
}

/* The save heart in its white disc, drawn once here as a mask rather than
   repeated six times in the markup. Same heart as the real cards. */
.hero-card-img i {
  position: absolute;
  top: 2.5cqw;
  right: 2.5cqw;
  width: 7cqw;
  height: 7cqw;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.9);
}

.hero-card-img i::after {
  content: "";
  position: absolute;
  inset: 22%;
  background: var(--ink);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linejoin='round'%3E%3Cpath d='M12 20.3 4.9 13.4a4.3 4.3 0 0 1 0-6.1 4.3 4.3 0 0 1 6.1 0l1 1 1-1a4.3 4.3 0 0 1 6.1 0 4.3 4.3 0 0 1 0 6.1L12 20.3z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linejoin='round'%3E%3Cpath d='M12 20.3 4.9 13.4a4.3 4.3 0 0 1 0-6.1 4.3 4.3 0 0 1 6.1 0l1 1 1-1a4.3 4.3 0 0 1 6.1 0 4.3 4.3 0 0 1 0 6.1L12 20.3z'/%3E%3C/svg%3E") center / contain no-repeat;
}

.hero-card-name {
  padding: 2.5cqw 2.5cqw 0;
  overflow: hidden;
  font-size: 4.2cqw;
  font-weight: 500;
  line-height: 1.3;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.hero-card-meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 2cqw;
  padding: 0.8cqw 2.5cqw 3cqw;
  font-size: 4cqw;
  line-height: 1.3;
}

.hero-card-meta b {
  font-weight: 600;
}

.hero-card-meta span {
  font-size: 3.6cqw;
  color: var(--ink-soft);
}

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

.hero-steps button {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: 12px;
  width: 100%;
  padding: 14px 0;
  border: 0;
  border-top: 1px solid var(--line);
  background: none;
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--ink-muted);
  text-align: left;
  cursor: pointer;
  transition: color 0.2s;
}

.hero-steps li:last-child button {
  border-bottom: 1px solid var(--line);
}

.hero-steps button.on {
  color: var(--ink);
}

.hero-steps b {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 400;
  line-height: 1;
}

/* The line along the lit step's top edge, running out the time until the
   phone moves on. Its length is app.js's STEP_MS for that step, handed over
   as --step-ms; 3.4s is only the fallback. */
.hero-bar {
  position: absolute;
  top: -1px;
  left: 0;
  width: 100%;
  height: 1px;
}

.hero-bar::after {
  content: "";
  display: block;
  width: 0;
  height: 100%;
  background: var(--ink);
}

.hero-steps button.on .hero-bar::after {
  animation: herobar var(--step-ms, 3.4s) linear forwards;
}

@keyframes herobar {
  to {
    width: 100%;
  }
}

/* ---- The tray of palettes ---- */

/* One ruled row per season: its name, its three palettes as tiles with a ring
   of their own five colors, and "Select all" at the end. --rs is the ring
   size. */
.hero-tray {
  --rs: 46px;
  border-bottom: 1px solid var(--line);
}

/* Every column a fixed share, the last one included, so the tiles line up
   straight down the tray. With the last column sized to its words, "Shop all
   Autumn" and "Shop all Spring" left the rows different room and no two rows'
   tiles started in the same place. */
.hero-tray-row {
  display: grid;
  grid-template-columns: 92px repeat(3, minmax(0, 1fr)) 88px;
  align-items: center;
  gap: 8px;
  padding: 11px 0;
  border-top: 1px solid var(--line);
}

.hero-tray-row h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 400;
  line-height: 1.1;
}

/* The season's name is a button too: it ticks all three palettes in the row.
   It keeps the look of a heading and only gives itself away on hover, with
   the same underline the ring names use, so the row still reads as a name
   followed by three choices. */
.hero-tray-season {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.hero-tray-season:hover,
.hero-tray-season[aria-pressed="true"] {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
}

/* Each palette is a tile: a bordered card with its ring and its name, and a
   round box in the corner that fills with a tick when it is chosen. A bare
   ring with a thin circle drawn round it didn't say "chosen" clearly enough,
   and nothing about an unpressed ring said it could be ticked at all. A card
   that darkens its border, tints its ground and ticks its box is the way
   every shop says "you've selected this", so nobody has to learn it. */
.hero-tray-p {
  /* Stretched to the row, so the three tiles are one height even though
     Light has no other name under it. Contents from the top, so the rings
     still sit level. */
  align-self: stretch;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 4px;
  min-width: 0;
  padding: 8px 4px 6px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--card);
  font-family: var(--font-body);
  font-size: 13.5px;
  color: var(--ink);
  cursor: pointer;
  transition: border-color 0.15s, box-shadow 0.15s, background-color 0.15s;
}

.hero-tray-p:hover {
  border-color: var(--ink-muted);
}

/* Ticked: an ink border twice as heavy (the second pixel is a shadow, so
   nothing inside moves) and a warm ground. Not a bold name: bold is wider,
   and "Bright (Clear)" broke onto a second line when ticked and made the
   whole row jump. --card is set here
   as well because the thin gaps between a ring's arcs are painted in it, and
   left white they would show as white nicks on the tinted ground. */
.hero-tray-p[aria-pressed="true"] {
  --card: var(--picked);
  border-color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--ink);
  background: var(--card);
}

/* The box in the corner: an empty circle until ticked, then ink with a white
   tick. The tick is drawn, not written, so no symbol text lands in the
   page. */
.hero-tray-p::after {
  content: "";
  position: absolute;
  top: 5px;
  right: 5px;
  width: 15px;
  height: 15px;
  box-sizing: border-box;
  border: 1.5px solid var(--line);
  border-radius: 50%;
  background: var(--card);
  transition: background-color 0.15s, border-color 0.15s;
}

.hero-tray-p:hover::after {
  border-color: var(--ink-muted);
}

.hero-tray-p[aria-pressed="true"]::after {
  border-color: var(--ink);
  background: var(--ink)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.5 8.2l2.3 2.3 4.7-4.9' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    center / 100% no-repeat;
}

/* The name and the other name, one under the other. */
.hero-tray-txt {
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* The other name, tucked under the first: smaller and grey, so the row still
   reads Bright, True, Dark at a glance. The negative margin pulls it up to
   the name above so the two read as one label. */
.hero-tray-alt {
  margin-top: -3px;
  font-size: 11.5px;
  color: var(--ink-muted);
}

/* Holds the ring at its own size, so the tile's height is set before the
   colors arrive. */
.hero-ring-wrap {
  display: grid;
  place-items: center;
  width: var(--rs);
  height: var(--rs);
}

/* The ring: a disc painted by app.js with five arcs of the palette's colors,
   with its middle cut out by the mask. Pale grey until the colors arrive. */
.hero-ring {
  width: var(--rs);
  height: var(--rs);
  border-radius: 50%;
  background: var(--line-light);
  -webkit-mask: radial-gradient(circle, transparent 40%, #000 41%);
  mask: radial-gradient(circle, transparent 40%, #000 41%);
  transition: transform 0.2s ease;
}

.hero-tray-p:hover .hero-ring {
  transform: rotate(18deg);
}

/* "Select all": a small outlined pill that fills in ink, with a tick, once
   all three of its row are ticked — the same on/off look as the tiles'
   corner boxes, so it plainly belongs to the same choosing. */
.hero-tray-all {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 5px 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  font-family: var(--font-body);
  font-size: 12.5px;
  color: var(--ink);
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}

.hero-tray-all:hover {
  border-color: var(--ink-muted);
}

.hero-tray-all[aria-pressed="true"] {
  border-color: var(--ink);
  background: var(--ink);
  color: #fff;
}

.hero-tray-all[aria-pressed="true"]::before {
  content: "";
  width: 11px;
  height: 11px;
  margin-left: -2px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 8.4l3.2 3.2L13 4.8' fill='none' stroke='white' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    center / 100% no-repeat;
}

/* The shop button under the tray, the same black as "Take the color test" on
   the other half, because it is the other half's equivalent: the one thing to
   press. It is there only once something is ticked. Its row keeps its height
   when it isn't, so the first tick fades it in rather than pushing the
   picker down.
   Sticky to the bottom of the screen: on a laptop the tray runs past the
   fold, and a button that appeared out of sight would be no button at all.
   Once the tray has been scrolled past, it settles back under it. The paper
   behind it hides the rings it rides over. */
.hero-go-row {
  position: sticky;
  bottom: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: 46px;
  margin-top: 4px;
  padding: 12px 0;
  background: var(--paper);
  opacity: 0;
  visibility: hidden;
  transform: translateY(4px);
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
}

.hero-go-row.on {
  opacity: 1;
  visibility: visible;
  transform: none;
}

.hero-go {
  padding: 12px 24px;
  border: 0;
  border-radius: var(--radius);
  background: var(--ink);
  color: #fff;
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
  transition: opacity 0.15s;
}

.hero-go:hover {
  opacity: 0.85;
}

/* Clear, as a real button beside Shop: the same height and size of type, in
   outline rather than black so Shop is still the one to press. As a small
   grey underlined word it was easy to miss. */
.hero-go-clear {
  padding: 11px 22px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
  cursor: pointer;
  transition: border-color 0.15s;
}

.hero-go-clear:hover {
  border-color: var(--ink);
}

.hero-note {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin: auto 0 0;
  padding-top: 22px;
  font-size: 13px;
  color: var(--ink-muted);
}

/* The way to the whole store, every color, for anyone who would rather not
   choose. */
/* "Or scroll to browse everything", centered along the bottom of the picker.
   Quiet on purpose: the picker is the invitation, and this is the polite
   note that there is another way in. The halves above give up some of their
   padding, top and bottom, to make room for it and for the description over
   them (.hero-intro), so as much of the picker as possible fits on a laptop
   screen. */
.hero .hero-side {
  padding-top: clamp(24px, 2.8vw, 36px);
  padding-bottom: 20px;
}

.hero-browse {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  align-self: center;
  padding: 8px 16px 14px;
  border: 0;
  background: none;
  font: inherit;
  font-size: 13px;
  color: var(--ink-soft);
  cursor: pointer;
}

.hero-browse:hover {
  color: var(--ink);
}

.hero-browse:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
  border-radius: 4px;
}

.hero-browse[hidden] {
  display: none;
}

.hero-browse-arrow {
  font-size: 16px;
  line-height: 1;
}

.hero-all {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.hero-steps button:focus-visible,
.hero-tray-p:focus-visible,
.hero-tray-all:focus-visible,
.hero-tray-season:focus-visible,
.hero-go:focus-visible,
.hero-go-clear:focus-visible,
.hero-all:focus-visible,
.hero-cta:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .hero-scr,
  .hero-ring,
  .hero-tray-p,
  .hero-tray-p::after,
  .hero-tray-all,
  .hero-go-row,
  .hero-steps button {
    transition: none;
  }

  .hero-steps button.on .hero-bar::after,
  .hero-shop.on .hero-shop-feed {
    animation: none;
  }
}

/* A laptop sees the whole picker at once, both halves and the cue under them,
   without scrolling: a front page that has to be scrolled to be read hides
   half of what it is offering. Below a tall monitor the screen's height, not
   its width, is what runs out, so the tall things (the phone, the rings, the
   gaps between rows) are sized against the height of the window (vh) and
   stop growing once there is room. Measured to fit at 1280×800, the usual
   small laptop. A phone stacks the halves and scrolls whatever happens, so it
   keeps its own sizes, below. */
@media (min-width: 768px) {
  .hero-intro {
    padding-top: clamp(10px, 1.6vh, 24px);
    padding-bottom: clamp(8px, 1.4vh, 20px);
  }

  .hero-intro-title {
    margin-bottom: 6px;
    font-size: clamp(26px, min(2.6vw, 4vh), 40px);
  }

  .hero .hero-side {
    padding-top: clamp(16px, 2.6vh, 36px);
    padding-bottom: clamp(6px, 1vh, 20px);
  }

  .hero-eyebrow {
    margin-bottom: 6px;
  }

  .hero-heading {
    margin-bottom: 8px;
    font-size: clamp(26px, min(2.8vw, 4.4vh), 42px);
  }

  .hero-sub {
    margin-bottom: clamp(12px, 2vh, 24px);
  }

  /* The phone is twice as tall as it is wide, so its width sets most of the
     left half's height. */
  .hero-demo {
    grid-template-columns: minmax(0, min(168px, 18vh)) minmax(0, 1fr);
    margin-bottom: clamp(14px, 2.4vh, 28px);
  }

  .hero-steps button {
    padding: clamp(10px, 1.6vh, 14px) 0;
  }

  .hero-tray {
    --rs: clamp(32px, 4.4vh, 46px);
  }

  .hero-tray-row {
    padding: clamp(3px, 0.6vh, 11px) 0;
  }

  /* The tiles' padding shrinks with the window's height too: four rows of
     tiles are what decide whether the panel fits the first screen. */
  .hero-tray-p {
    gap: 2px;
    padding: clamp(4px, 0.7vh, 8px) 4px clamp(3px, 0.5vh, 6px);
  }

  /* Side by side here, "Bright (Clear)", to save a line in every row. Where a
     column is too narrow for both, the other name wraps back underneath. */
  .hero-tray-txt {
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
    align-items: baseline;
    column-gap: 4px;
  }

  .hero-tray-alt {
    margin-top: 0;
  }

  .hero-go-row {
    min-height: 40px;
    padding: 8px 0;
  }

  .hero-go {
    padding: 10px 22px;
  }

  .hero-go-clear {
    padding: 9px 18px;
  }

  .hero-note {
    padding-top: 10px;
  }

  .hero-browse {
    padding: 2px 16px clamp(6px, 1vh, 14px);
  }
}

/* A phone: the halves stack, the find-your-season half first, and the phone
   and its steps shrink to sit side by side in the narrow width. Each tray row
   puts the season and "Shop all" on one line and the three rings under it, so
   the rings keep a size a thumb can hit. */
@media (max-width: 767px) {
  .hero {
    grid-template-columns: 1fr;
  }

  .hero-side + .hero-side {
    border-left: 0;
    border-top: 1px solid var(--line);
  }

  .hero-cta {
    align-self: stretch;
    text-align: center;
  }

  .hero-demo {
    grid-template-columns: 112px minmax(0, 1fr);
    gap: 16px;
  }

  .hero-phone {
    padding: 4px;
    border-radius: 20px;
  }

  .hero-screen {
    border-radius: 16px;
  }

  .hero-result strong {
    font-size: 15px;
  }

  .hero-result small {
    font-size: 9px;
  }

  .hero-dots span {
    width: 10px;
    height: 10px;
  }

  .hero-steps button {
    gap: 8px;
    padding: 11px 0;
    font-size: 13.5px;
  }

  .hero-steps b {
    font-size: 18px;
  }

  .hero-corner {
    width: 10px;
    height: 10px;
  }

  .hero-shutter {
    width: 20px;
    height: 20px;
    margin-left: -10px;
  }

  .hero-tray {
    --rs: 40px;
  }

  .hero-tray-row {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    row-gap: 8px;
  }

  .hero-tray-row h3 {
    grid-row: 1;
    grid-column: 1 / 4;
    font-size: 19px;
  }

  .hero-tray-all {
    grid-row: 1;
    grid-column: 3;
    justify-self: end;
  }

  .hero-tray-p {
    grid-row: 2;
  }

  /* Full width on a phone, like "Take the color test" above it. */
  .hero-go {
    flex: 1;
  }
}

/* A tablet, or a narrow laptop window: the two halves still sit side by side,
   but the right half is too narrow for the name, three tiles and "Select all"
   on one line. The tiles came out thinner than their rings, and the tick in
   each corner sat on the ring. So each row stacks the way it does on a phone:
   the season and "Select all" on one line, the three tiles under it. These
   screens are tall enough that the extra lines cost nothing. */
@media (min-width: 768px) and (max-width: 999px) {
  .hero-tray-row {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    row-gap: 6px;
  }

  .hero-tray-row h3 {
    grid-row: 1;
    grid-column: 1 / 4;
  }

  .hero-tray-all {
    grid-row: 1;
    grid-column: 3;
    justify-self: end;
  }

  .hero-tray-p {
    grid-row: 2;
  }
}

/* ---- Category chips ---- */

/* The way back to the shop from every page that is not the shop. It sits at the
   far end beside the four marks rather than next to the wordmark, because next
   to “ShopYourHue” the word Shop reads as an echo of it. Over here it reads as
   one more thing you can do. It is one link rather than a repeat of the
   category tabs, because a row of All / Dresses / Tops above a grid of saved
   items would read as a filter for those items. Its own auto margin takes the
   slack, and `.header-right` gives its up in return, so the two sit together at
   the edge with the header's gap between them. */
.header-shop-link {
  flex: none;
  margin-left: auto;
  padding: 6px 0;
  font-size: 14.5px;
  /* 21 + 6 + 6 = 33px, the wordmark's own height. Left to its default this text
     boxes at 33.75px and quietly makes every non-shop header 0.75px taller than
     the shop's, so the whole page under it steps down when you navigate. */
  line-height: 21px;
  color: var(--ink-soft);
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.15s;
}
.header-shop-link:hover {
  color: var(--ink);
}
.header-shop-link + .header-right {
  margin-left: 0;
}


.category-chips:empty {
  display: none; /* no categories in the data yet — don't leave a gap behind */
}

/* While the first catalogue fetch is in flight there is nothing to list yet.
   The row is held open anyway — its width so the header's other pieces don't
   slide sideways when the tabs land, and its height so the store's header is
   two rows tall from the first frame rather than growing by a row underneath
   the page once they do. The height is one tab: 6px padding either side of an
   18px line. */
.category-chips.pending:empty {
  display: flex;
  min-height: 30px;
}

/* Text tabs: no pills, no borders. The active one is near-black with the
   same underline the current nav link uses.

   These sit inside the header rather than in a bar of their own — one sticky
   bar of two rows, not two sticky bars. Two separate frozen bars took a real
   bite out of the screen; two rows of one bar cost a single line, and the tabs
   are still on screen after you scroll, which is the whole point of freezing
   them.

   Row one is the wordmark and the site's own marks at the far edge. Row two is
   the departments: the tabs, with Palette and Filters at the end of them.
   Crowded onto one line those two were small things in a right-hand cluster and
   read as more of the site's marks; on a row of their own they read as what
   they are. This is the shape the header already took on a phone — it is now
   the shape at every width.

   Palette sits out here rather than inside the tab row because the row scrolls
   sideways once the tabs stop fitting, and a control inside it goes off the
   edge with them. */
.category-chips {
  display: flex;
  align-items: center;
  gap: 22px;
  /* Wide enough that the tabs can never share row one with the wordmark and
     the marks, so the second row is a certainty rather than something that
     happens once the window is narrow enough. What is held back is what the
     two buttons need beside them on that row — Palette and Filters, with the
     header's 12px gap on each side of the pair. */
  order: 1;
  flex: 1 1 calc(100% - 240px);
  min-width: 0;
  margin-top: 4px;
  /* Sideways instead of onto a third line: the tabs are the one thing here that
     can run to any length. */
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none; /* the tabs are the affordance; a scrollbar under them
                            in the header reads as a stray rule */
}

.category-chips::-webkit-scrollbar {
  display: none;
}

/* A named palette ("Soft Autumn", with its dot) is wider than the one word
   "Palette", so with one chosen the tabs hold back more room for the pair, or
   Filters would be pushed onto a line of its own. app.js sets the class.
   Holding back more is what lets the tabs fit up on row one beside the
   wordmark, though, so the marks on the right stretch to fill the rest of that
   row (still sitting at its far end) and leave nothing up there for the tabs
   to fit into. */
.site-header.palette-picked .category-chips {
  flex-basis: calc(100% - 310px);
}

.site-header.palette-picked .header-right {
  flex: 1 0 calc(100% - 200px);
  justify-content: flex-end;
}

/* The rest of row two: the one control that cuts across every department sits
   at the far end of the tabs. */
.chip-filter {
  order: 1;
  margin-top: 4px;
}

/* Tighter spacing buys back a tab or two before the row starts scrolling. */
@media (max-width: 860px) {
  .category-chips {
    gap: 18px;
  }
}

.category-chips button {
  flex: none;
  white-space: nowrap;
  padding: 6px 0;
  font-family: inherit;
  font-size: 14.5px;
  color: var(--ink-soft);
  border: none;
  background: transparent;
  cursor: pointer;
  text-transform: capitalize;
  transition: color 0.15s;
}

.category-chips button:hover {
  color: var(--ink);
}

.category-chips button.active {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 5px;
}

/* Hairline between "All" and the categories. */
.chip-divider {
  align-self: center;
  flex: none;
  width: 1px;
  height: 16px;
  background: var(--line);
}

/* ---- Filters ---- */

/* The control at the end of the tabs. Price and coverage aren't departments, so
   they don't get tabs of their own — this opens a panel holding both, and keeps
   the row from growing every time a filter is added.

   Shaped like the palette chip beside it rather than like the site's own marks:
   an outlined pill with a word in it reads as something you press, where a bare
   icon in a row of bare icons reads as decoration. The two sit together at the
   end of row two as the pair of things that narrow the store.

   It is a child of the header rather than of the tab row, so it keeps its place
   when the row runs out of width and starts scrolling sideways: inside the row
   it was carried off the right edge on a narrow screen and the store looked
   like it had no filters at all. `flex: none` is what holds that place — the
   tabs beside it are the thing that gives up room. */
.chip-filter {
  position: relative;
  display: flex;
  flex: none;
  align-items: center;
  gap: 6px;
  /* Same height and rounding as .palette-chip, so the two read as one pair
     rather than two unrelated controls that happen to be adjacent. */
  padding: 5px 12px;
  font: inherit;
  font-size: 0.9rem;
  line-height: 1.2;
  color: var(--ink-soft);
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}

.chip-filter svg {
  flex: none;
  width: 17px;
  height: 17px;
}

.chip-filter:hover {
  color: var(--ink);
  background: var(--paper);
}

/* Open, or filtering: either way the control goes to full ink, the same way the
   selected tab does. */
.chip-filter.open,
.chip-filter.active {
  color: var(--ink);
  border-color: var(--ink-soft);
}

.chip-filter.open {
  background: var(--paper);
}

.chip-filter:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* How many filters are on. A filled numeral rather than a dot: a dot says only
   that something is narrowing the store, and the shopper looking at a short
   grid wants to know whether that is one thing or both. Hidden at zero — app.js
   sets the `hidden` attribute, and this is the shape it takes when shown. */
.chip-filter-count {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--ink);
  color: var(--card);
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  /* The numeral's own box, so a 1 and a 2 are the same width and the pill does
     not change size as filters go on and off. */
  font-variant-numeric: tabular-nums;
}

/* `display: flex` above outranks the browser's own rule for the attribute, so
   without this the badge sits there reading "0". */
.chip-filter-count[hidden] {
  display: none;
}

/* The panel itself. app.js hangs it under its button and gives it its width;
   everything here is what it looks like once placed. Above the header (30) so
   it isn't clipped by it, below the detail overlay (50) and the bottom bar
   (60), which are both whole-screen and should cover it. */
.filter-panel {
  position: fixed;
  z-index: 45;
  padding: 16px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  /* A real shadow, not a hairline: this floats over the grid, and the cards
     under it are white too. */
  box-shadow: 0 10px 28px rgba(36, 36, 36, 0.13);
}

.filter-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 14px;
}

.filter-title {
  font-family: var(--font-display);
  font-size: 16px;
  color: var(--ink);
}

.filter-reset {
  padding: 0;
  font-family: inherit;
  font-size: 13px;
  color: var(--ink-muted);
  background: none;
  border: none;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.filter-reset:hover:not(:disabled) {
  color: var(--ink);
}

/* Nothing to put back — greyed rather than hidden, so the panel doesn't change
   shape as filters go on and off. */
.filter-reset:disabled {
  opacity: 0.4;
  cursor: default;
  text-decoration: none;
}

/* The rule between price and the toggle: the panel holds two separate
   questions and shouldn't read as one list. */
.filter-section-split {
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--line-light);
}

.filter-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 10px;
}

.filter-label {
  font-size: 14px;
  color: var(--ink);
}

.filter-readout {
  font-size: 13.5px;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums; /* the number stops twitching mid-drag */
}

.filter-readout.is-set {
  color: var(--ink);
}

.filter-result-count {
  margin: 16px 0 0;
  font-size: 12.5px;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}

/* ---- The palette, at the top of the panel ---- */

/* The palette throws out more of the store than price and modesty put
   together, so it belongs in the list of what is narrowing the grid, and at the
   top of it. It was the one thing doing the filtering that this panel never
   mentioned.

   It is not chosen here. Twelve palettes want room, and they already have a
   picker with the colors drawn out; this is the way in, not a second copy of
   it. A whole button so the name, the colors and the line underneath are all
   one press, stripped of its own look because the panel gives it one. */
.filter-palette {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.filter-palette .filter-row {
  align-items: center;
  margin-bottom: 0;
}

/* The name and its chevron travel together on the right of the row, opposite
   the word Palette, the way the price reads out opposite the word Price. */
.filter-palette-value {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

/* The one thing in this row that says it goes somewhere. It does the work the
   sentence underneath used to do, in the space of a character. */
.filter-palette-chevron {
  flex: none;
  font-size: 15px;
  line-height: 1;
  color: var(--ink-muted);
  transform: translateY(-0.5px);
}

.filter-palette:hover .filter-palette-chevron,
.filter-palette:focus-visible .filter-palette-chevron {
  color: var(--ink);
}

.filter-palette:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 5px;
  border-radius: 3px;
}

/* Ruled off below rather than above: it is the first thing in the panel, and
   the rule separates it from price the way the split rule separates modesty. */
.filter-section-palette {
  margin-bottom: 20px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--line-light);
}

/* ---- The price range ---- */

/* Two sliders lying on top of each other, one handle live on each. The track
   and the lit span between the handles are drawn by the spans underneath; the
   inputs are invisible and only their handles take the pointer, which is what
   lets both ends be dragged on one line. */
.range {
  position: relative;
  height: 22px;
  /* Full width, with no inset of its own. The browser never lets a handle out
     past the ends of its slider, so at $0 the handle's edge is the slider's
     edge — and that is what lines it up under the P of "Price" and the right
     end with the price and the switch below. An inset here pushed both handles
     in from everything else in the panel. */
  margin: 0 0 2px;
}

.range-track,
.range-fill {
  position: absolute;
  top: 50%;
  height: 3px;
  margin-top: -1.5px;
  border-radius: 2px;
}

/* The line runs between the handles' centres, not edge to edge: a handle held
   inside its slider travels from 8px in to 8px in (half its 16px width), so a
   line out to the very ends would poke out past a handle sitting at $0. */
.range-track {
  left: 8px;
  right: 8px;
  background: var(--line-light);
}

.range-fill {
  background: var(--ink);
}

.range-input {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 22px;
  margin: 0;
  background: none;
  appearance: none;
  -webkit-appearance: none;
  /* The two inputs overlap completely, so the whole surface of the top one
     would otherwise swallow every click meant for the one beneath. Only the
     handles are given the pointer back. */
  pointer-events: none;
}

.range-input::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  pointer-events: auto;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--card);
  border: 1.5px solid var(--ink);
  box-shadow: 0 1px 3px rgba(36, 36, 36, 0.2);
  cursor: grab;
}

.range-input::-moz-range-thumb {
  pointer-events: auto;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--card);
  border: 1.5px solid var(--ink);
  box-shadow: 0 1px 3px rgba(36, 36, 36, 0.2);
  cursor: grab;
}

.range-input:active::-webkit-slider-thumb {
  cursor: grabbing;
}

.range-input:focus-visible::-webkit-slider-thumb {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

.range-input:focus-visible::-moz-range-thumb {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

.range-input:focus {
  outline: none; /* the ring belongs on the handle, not the full-width input */
}

/* ---- The modest toggle ---- */

.switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.switch-row label {
  cursor: pointer;
}

/* Positioned so the explanation can hang off it. */
.switch-text {
  position: relative;
  display: flex;
  align-items: center;
  gap: 2px;
  min-width: 0;
}

.switch-name {
  font-size: 14px;
  color: var(--ink);
}

/* The "?" in a circle beside "Modest only". What the word means here is kept
   behind it rather than printed under the switch, so the panel stays two
   plain controls; the explanation is one hover, focus or tap away.
   Small and hung from the top of the capitals, the way a note mark sits: its
   top edge is level with the top of the "M" and it never rises past it, so it
   reads as a footnote to "Modest only" rather than a control of its own. The
   margin is where the cap line falls inside the 21px line box at 14px. */
.info-tip {
  position: relative;
  flex: none;
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 8px;
  height: 8px;
  margin-top: 6px;
  margin-left: 1px;
  padding: 0;
  border: 1px solid var(--ink-muted);
  border-radius: 50%;
  background: none;
  color: var(--ink-muted);
  font-family: inherit;
  font-size: 6px;
  font-weight: 700;
  line-height: 1;
  cursor: help;
}

/* The mark is too small to hit reliably with a finger, so it answers to a
   larger square around it than it draws. */
.info-tip::before {
  content: "";
  position: absolute;
  inset: -8px;
}

.info-tip:focus {
  outline: none;
}

.info-tip:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* Opens upward, over the price section, so it stays inside the panel rather
   than hanging off its bottom edge over the grid. */
.info-tip-text {
  position: absolute;
  left: 0;
  bottom: calc(100% + 8px);
  z-index: 1;
  width: 240px;
  max-width: calc(100vw - 64px);
  padding: 8px 10px;
  border-radius: 4px;
  background: var(--ink);
  color: var(--paper);
  font-size: 12px;
  line-height: 1.45;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease, visibility 0.12s;
}

.info-tip:focus-visible + .info-tip-text,
.switch-text.tip-open .info-tip-text {
  opacity: 1;
  visibility: visible;
}

/* Hover only where there is a real pointer: a phone's tap leaves :hover stuck
   on, which would keep the explanation up after the tap meant to close it. */
@media (hover: hover) {
  .info-tip:hover {
    color: var(--ink);
    border-color: var(--ink);
  }

  .info-tip:hover + .info-tip-text {
    opacity: 1;
    visibility: visible;
  }
}

.switch {
  position: relative;
  flex: none;
  width: 38px;
  height: 22px;
  margin-top: 1px;
}

.switch input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.switch-track {
  position: absolute;
  inset: 0;
  border-radius: 11px;
  background: var(--line);
  transition: background 0.18s ease;
}

.switch-track::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--card);
  transition: transform 0.18s ease;
}

.switch input:checked + .switch-track {
  background: var(--ink);
}

.switch input:checked + .switch-track::after {
  transform: translateX(16px);
}

.switch input:focus-visible + .switch-track {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .switch-track,
  .switch-track::after,
  .chip-filter {
    transition: none;
  }
}

/* ---- Grid ---- */

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 16px;
  padding: 20px;
  opacity: 1;
  transition: opacity 0.18s ease;
}

@media (min-width: 640px) {
  .grid {
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  }
}

.grid.fading {
  opacity: 0;
}

/* The card is a frame around two buttons: `.card-open` (photo + text, opens
   the detail view) and `.fav-btn` (the heart in the photo's corner). */
.card {
  position: relative;
  background: var(--card);
  border: 1px solid var(--line-light);
  border-radius: var(--radius);
  overflow: hidden;
  transition: transform 0.15s, box-shadow 0.15s, opacity 0.18s ease;
  display: flex;
  flex-direction: column;
}

.card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
}

/* Un-hearted on the favorites page: fades before the grid closes the gap. */
.card.leaving {
  opacity: 0;
  pointer-events: none;
}

.card-open {
  display: flex;
  flex-direction: column;
  flex: 1;
  width: 100%;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
}

.card-open:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: -2px;
}

/* ---- Heart (save to favorites) ---- */

/* A small white disc in the photo's top-right corner, the same size as the
   overlay's close button. Outline heart when not saved, filled in ink when
   saved — no red, nothing that competes with the garment's colour. */
.fav-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 34px;
  height: 34px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.9);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.15s;
}

.fav-btn:hover {
  background: #fff;
}

.fav-btn:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

.fav-btn svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linejoin: round;
  transition: transform 0.15s ease;
}

.fav-btn[aria-pressed="true"] svg {
  fill: currentColor;
}

.fav-btn:active svg {
  transform: scale(0.88);
}

/* In the detail view the heart sits top-left, opposite the close button. */
.card img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  background: var(--line);
}

/* Loading blanks (first visit of a session, before the catalogue arrives):
   the card frame with a flat grey image block and two short text bars, no
   animation. Same dimensions as a real card so nothing shifts on swap. */
.card-placeholder {
  cursor: default;
  pointer-events: none;
}

.placeholder-img {
  width: 100%;
  aspect-ratio: 3 / 4;
  background: var(--line-light);
}

.placeholder-line {
  display: block;
  height: 10px;
  width: 40%;
  border-radius: 4px;
  background: var(--line-light);
  margin: 4px 0 8px;
}

.placeholder-line-long {
  width: 70%;
  margin-bottom: 6px;
}

/* Every card's text block is identical in height: one brand line, one name
   line (ellipsized), one price/category row. Nothing wraps, so brand, name
   and price land at the same height on every card and no card is ragged. */
.card-info {
  padding: 12px 12px 14px;
  display: flex;
  flex-direction: column;
  flex: 1;
  text-align: left;
}

/* The brand is a small caption; the product name sits 4px under it. */
.card-brand {
  margin: 0 0 4px;
  font-size: 11.5px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
  line-height: 1.4;
  min-height: 1.4em; /* holds the line even when the brand is empty */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* One line, cut off with an ellipsis. The full name is in the card's title
   attribute (tooltip) and in the pop-up. */
.card-name {
  margin: 0 0 6px;
  font-size: 14.5px;
  font-weight: 500;
  color: var(--ink);
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Price on the left, category on the right — always exactly one line,
   6px under the name. No bottom-pinning needed: every line above it is
   one line tall, so it lands at the same height on every card. */
.card-meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  line-height: 1.4;
  min-height: 1.4em; /* holds the line even when price and category are empty */
  white-space: nowrap;
}

.card-price {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink);
}

.card-category {
  margin-left: auto; /* stays on the right even when there is no price */
  font-size: 12px;
  color: var(--ink-soft);
  text-transform: capitalize;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---- Empty state ---- */

.empty-state {
  display: none;
  padding: 60px 20px;
  text-align: center;
  color: var(--ink-soft);
  font-size: 1rem;
}

.empty-state.visible {
  display: block;
}

.empty-state p {
  margin: 0 0 6px;
}

.empty-state-title {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: 1.35rem;
  color: var(--ink);
}

.empty-state-link {
  display: inline-block;
  margin-top: 14px;
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 5px;
}

/* ---- Favorites page ---- */

/* 44px, not 36, so the h1 here starts at exactly the height it starts at on
   every other page. The two used to differ by 8px, which read as the page
   jumping when you moved between Favorites and, say, a season. */
.page-head {
  padding: 44px 20px 0;
}

.page-head-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
}

.page-head h1 {
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: clamp(32px, 4vw, 44px);
  font-weight: 400;
  line-height: 1.1;
}

.text-btn {
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  font-size: 0.95rem;
  color: var(--ink);
  cursor: pointer;
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 5px;
}

.text-btn:hover {
  color: var(--ink-soft);
}

.text-btn:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

.page-note {
  margin: 0;
  max-width: 560px;
  font-size: 0.97rem;
  color: var(--ink-soft);
}

/* ---- Detail view (overlay) ---- */

/* Applied by app.js while the detail card is open. `position: fixed` is the
   part that holds on iOS Safari, where `overflow: hidden` alone is ignored;
   app.js supplies the matching `top` offset and restores it on close. */
body.scroll-locked {
  position: fixed;
  width: 100%;
  overflow: hidden;
}

/* Hidden with opacity + visibility rather than display:none so the open and
   close can fade. `visibility: hidden` takes the overlay out of the tab order
   and the accessibility tree; on close it is delayed until the fade finishes.
   pointer-events guards against clicks landing on a half-faded backdrop. */
.detail-overlay {
  display: flex;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 50;
  align-items: center;
  justify-content: center;
  padding: 20px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.15s ease, visibility 0s linear 0.15s;
}

.detail-overlay.visible {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 0.15s ease, visibility 0s;
}

/* Column flex so the card fits the viewport by construction: the image is the
   only part allowed to shrink, so the brand/name/price/Shop block is always
   whole and the Shop button never falls below the fold. Scrolling to reach the
   one button that earns money is the fastest way to lose a sale. */
.detail-card {
  background: var(--card);
  border-radius: var(--radius);
  max-width: 400px;
  width: 100%;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  position: relative;
  transform: translateY(6px);
  transition: transform 0.15s ease;
}

.detail-overlay.visible .detail-card {
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .detail-overlay,
  .detail-card,
  .card,
  .fav-btn svg {
    transition: none;
  }
}

/* aspect-ratio is the preferred height, not a floor — on a short viewport the
   photo crops a little further rather than pushing the button out of sight.
   min-height keeps it from collapsing to a sliver on very short screens; below
   that the card falls back to scrolling. */
.detail-card img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  flex: 0 1 auto;
  min-height: 200px;
}

.detail-info {
  padding: 16px 20px 20px;
  flex: 0 0 auto;
  text-align: left;
}

.detail-brand {
  margin: 0 0 3px;
  font-size: 12px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
}

.detail-brand[hidden] {
  display: none;
}

.detail-info h2 {
  margin: 0 0 4px;
  font-size: 1.15rem;
  font-weight: 500;
}

.detail-price {
  margin: 0 0 16px;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--ink);
}

.shop-btn {
  display: block;
  width: 100%;
  padding: 13px;
  font-size: 1rem;
  font-weight: 500;
  text-align: center;
  text-decoration: none;
  color: #fff;
  background: var(--ink);
  border-radius: var(--radius);
  transition: background 0.15s;
}

.shop-btn:hover {
  background: #0f0f0f;
}

/* Item with no affiliate link: the button stays in place but is inert. */
.shop-btn[aria-disabled="true"] {
  background: var(--line-light);
  color: var(--ink-muted);
  cursor: default;
  pointer-events: none;
}

/* The commission line under Shop. Small and quiet, but in the pop-up itself. */
.detail-disclosure {
  margin: 8px 0 0;
  font-size: 0.75rem;
  line-height: 1.4;
  text-align: center;
  color: var(--ink-muted);
}

.detail-disclosure a {
  color: inherit;
}

/* No retailer link, nothing to disclose. */
.shop-btn[aria-disabled="true"] + .detail-disclosure {
  display: none;
}

.detail-close {
  position: absolute;
  top: 10px;
  left: 10px;
  width: 34px;
  height: 34px;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.9);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}

/* ---- Shared chrome (header nav + footer), injected by components.js ---- */

a.site-title {
  color: inherit;
  text-decoration: none;
}

.header-right {
  display: flex;
  align-items: center;
  gap: 12px 18px;
  flex-wrap: wrap;
  /* Never gives up width to the category tabs beside it, and never gets pushed
     onto a line of its own: the tabs scroll sideways instead, and below 860px
     they take their own line while this stays up top with the wordmark. */
  flex: none;
  margin-left: auto;
}

/* The four marks in the bar. `position: relative` is here so each one can
   hang its own name underneath it on hover. */
.header-icon {
  position: relative;
  display: flex;
  align-items: center;
  color: var(--ink-soft);
  padding: 2px 0;
}

.header-icon svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.header-icon:hover {
  color: var(--ink);
}

/* The page you are on. Without words there is no underline to thicken, so the
   mark goes to full ink and takes a dot beneath it — the same weight of hint
   the underline used to give, in the space an icon leaves free. */
.header-icon.current {
  color: var(--ink);
}

.header-icon.current::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -5px;
  width: 3px;
  height: 3px;
  margin-left: -1.5px;
  border-radius: 50%;
  background: var(--ink);
}

/* The heart fills on its own page, and the head fills once you are signed in,
   so a filled icon is how this bar says "you are here". */
.header-icon.filled svg {
  fill: currentColor;
}

/* The clock does the same. Filled plainly it would be a black dot with the
   hands gone, so the face fills and the hands turn the colour of the header —
   a solid clock you can still read the time on. */
.header-icon.current[href="/recent.html"] svg path:first-child {
  fill: currentColor;
}

.header-icon.current[href="/recent.html"] svg path:last-child {
  stroke: var(--card);
  stroke-width: 1.8;
}

.header-icon:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 3px;
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 18px;
}

/* ---- The name that appears under an icon on hover ----
   Icons are quick to scan and slow to learn, so each one says what it is the
   moment you point at it. This is drawn rather than left to the browser's
   own `title` tooltip, which waits about a second, cannot be styled, and on
   some systems appears in a corner far from the thing you are pointing at.
   Gated behind `hover: hover` so a phone, where there is no hovering and a
   long press means something else, never has to deal with it. */
@media (hover: hover) {
  .header-icon[data-label]::after,
  .account-btn[data-label]::after {
    content: attr(data-label);
    position: absolute;
    top: calc(100% + 10px);
    left: 50%;
    transform: translateX(-50%);
    padding: 4px 8px;
    border-radius: 4px;
    background: var(--ink);
    color: var(--paper);
    font-family: var(--font-body);
    font-size: 11px;
    letter-spacing: 0.02em;
    line-height: 1.3;
    white-space: nowrap;
    opacity: 0;
    /* Hidden names must not swallow clicks meant for the page underneath. */
    pointer-events: none;
    transition: opacity 0.12s ease 0.05s;
  }

  .header-icon:hover::after,
  .header-icon:focus-visible::after,
  .account-btn:hover::after,
  .account-btn:focus-visible::after {
    opacity: 1;
  }

  /* The account sits at the far right, so its name is pinned to that edge
     instead of centred, or half of it would hang off the screen. */
  .account-btn[data-label]::after {
    left: auto;
    right: 0;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .header-icon[data-label]::after,
  .account-btn[data-label]::after {
    transition: none;
  }
}

/* Phone header (640px and narrower). Row one holds the wordmark on the left,
   the palette chip beside it and the four marks on the right — that fits a
   375px screen now that none of the marks is a word. In the store the category
   tabs take the line under it, so the bar is two rows there and one row
   everywhere else. */
@media (max-width: 640px) {
  #site-header {
    min-height: 63px;
  }

  .site-header {
    padding-left: 16px;
    padding-right: 16px;
  }

  .site-title {
    font-size: 20px;
  }

  /* 18 + 6 + 6 = 30px, this size's wordmark height, so the Shop link still is
     not what decides how tall the bar is. */
  .header-shop-link {
    line-height: 18px;
  }

  .site-nav {
    gap: 16px;
  }

  /* Two rows on a phone as well, now that the palette no longer needs one of
     its own: the tabs and, at the end of them, Filters. The tabs are held back
     by what the two buttons need — Filters comes to about 60px once the word
     is dropped below, and Palette keeps its word because it has no mark to
     stand in for one — and they scroll for the rest. Tabs are the one thing
     here that can be scrolled to; a button that has scrolled off the edge is
     simply gone.

     The padding on the right is the room the last tab needs to clear the fade
     once you have scrolled to the end of them. */
  .category-chips {
    order: 1;
    flex: 1 1 calc(100% - 180px);
    margin-top: 2px;
    padding-right: 28px;
    gap: 18px;
    /* The other half of the cue: the tabs thin out into the Filters button
       instead of stopping short of it, the way the last line before a page turn
       does. Nothing to read and nothing to tap — it only has to look
       unfinished. */
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), rgba(0, 0, 0, 0));
    mask-image: linear-gradient(90deg, #000 calc(100% - 28px), rgba(0, 0, 0, 0));
  }

  /* Beside Filters, same as on a wide screen. It keeps its word where Filters
     drops one, because Filters has a mark that says what it is and this has
     nothing but the word. Tighter in, to buy the tabs back what that costs. */
  .palette-chip {
    gap: 3px;
    padding: 5px 8px 5px 10px;
  }

  /* No room for the word up here: spelling it out costs a third of what is left
     for the tabs, and they are already scrolling. The outline and the count
     still say this is a control rather than a mark, and a phone shopper is the
     one most used to reading these two rules as filters. */
  .chip-filter {
    order: 1;
    margin-top: 2px;
    gap: 5px;
    padding: 5px 9px;
  }

  .chip-filter-label {
    display: none;
  }

  /* With a palette chosen, a phone has no room for its name beside the tabs:
     holding that much back would leave the tabs about one word wide. So the
     tabs take the whole of row two, and the palette and Filters get a row of
     their own under them — the palette at the start, where it is read first,
     Filters at the far end. The header is a line taller, which is the price of
     the palette saying what it is. */
  .site-header.palette-picked .category-chips {
    flex-basis: 100%;
  }

  .site-header.palette-picked .palette-chip {
    max-width: calc(100% - 60px);
    margin-right: auto;
  }
}

.site-footer {
  margin-top: 48px;
  padding: 32px 20px 36px;
  border-top: 1px solid var(--line-light);
  background: var(--card);
  text-align: center;
  font-size: 0.9rem;
  color: var(--ink-soft);
}

.site-footer p {
  margin: 0 0 12px;
}

.footer-links {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 6px 18px;
  margin: 0 0 12px;
}

.footer-links a,
.footer-social a {
  color: var(--ink-soft);
}

.footer-links a:hover,
.footer-social a:hover {
  color: var(--ink);
}

.footer-social a {
  margin: 0 9px;
}

.footer-copyright {
  color: var(--ink-muted);
}

/* No width cap: the line reads as one straight sentence across the footer
   rather than being folded into a narrow block in the middle. It wraps on its
   own when the screen is too narrow to hold it. */
.footer-affiliate {
  font-size: 0.78rem;
  color: var(--ink-muted);
}

/* ---- Back to top ---- */

/* Sits above the cookie bar rather than on it: --cookie-h is measured and
   published by components.js and drops to 0 the moment the bar is dismissed.
   Under the cookie bar (60) and the detail overlay (50) in the stack — while a
   product is open there is nothing to scroll behind it. */
.to-top {
  position: fixed;
  right: 20px;
  bottom: calc(var(--cookie-h, 0px) + 20px);
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  font-size: 1.1rem;
  line-height: 1;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  /* Kept out of the tab order and off the screen reader while it is invisible;
     `hidden` would work too but cannot fade. */
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.18s, visibility 0.18s;
}

.to-top.visible {
  visibility: visible;
  opacity: 1;
}

.to-top:hover {
  background: var(--paper);
}

@media (prefers-reduced-motion: reduce) {
  .to-top {
    transition: none;
  }
}

/* ---- Cookie notice (thin top border, no shadow — matches the flat chrome) ---- */

.cookie-bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px 16px;
  flex-wrap: wrap;
  padding: 12px 20px;
  background: var(--card);
  border-top: 1px solid var(--line);
  font-size: 0.85rem;
}

.cookie-bar p {
  margin: 0;
  color: var(--ink-soft);
}

.cookie-bar a {
  color: var(--ink);
}

.cookie-bar button {
  padding: 7px 18px;
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  color: #fff;
  background: var(--ink);
  border: none;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background 0.15s;
}

.cookie-bar button:hover {
  background: #0f0f0f;
}

/* ---- Affiliate disclosure line above the grid ---- */

.affiliate-note {
  margin: 0;
  padding: 14px 20px 0;
  font-size: 0.8rem;
  color: var(--ink-muted);
}

.affiliate-note a {
  color: inherit;
}

/* ---- The filters that are on, above the grid ---- */

/* Down here with the grid and not up beside the Filters button: in the header
   a chip that grew and shrank with its label pushed the button along with it. */
.active-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 12px 20px 0;
}

.active-filters[hidden] {
  display: none;
}

.active-filter {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 10px 5px 12px;
  font: inherit;
  font-size: 0.85rem;
  line-height: 1.2;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--ink-soft);
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
}

.active-filter-x {
  font-size: 0.72rem;
  color: var(--ink-muted);
}

.active-filter:hover .active-filter-x,
.active-filter:focus-visible .active-filter-x {
  color: var(--ink);
}

/* ---- Static pages (about, contact, legal, seasons) ---- */

/* Centred, and capped at 720px so the lines stay short enough to read. Left
   against the edge it matched where the shop grid starts, but on a wide screen
   that left the whole right half of the page empty and the writing looked
   shoved into a corner. On a narrow screen there is no room to spare and the
   column starts at the same left edge either way, so nothing changes there. */
.page {
  max-width: 720px;
  margin: 0 auto;
  padding: 44px 20px 24px;
}

/* A page that is a grid of cards rather than a column of writing — the seasons
   index. It runs the full width of the screen, the way the shop grid does;
   only the writing at the top is held to a readable line length. */
.page-wide {
  max-width: none;
}

/* The introduction is one block in the middle, under the quiz box, with its
   text centered like the box's. Against the left edge it looked pushed over;
   as two columns, one paragraph sat left and the other right, and that looked
   odd too. Running the full width would make lines too long to follow, and
   a narrow column looked squeezed into the middle, so it sits in between. */
.page-wide > .page-intro {
  max-width: 88ch;
  margin: 0 auto;
  text-align: center;
}

.page h1 {
  margin: 0 0 20px;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: clamp(32px, 4vw, 44px);
  font-weight: 400;
  line-height: 1.1;
}

.page h2 {
  margin: 32px 0 10px;
  font-size: 1.15rem;
  font-weight: 600;
}

.page p,
.page li {
  color: var(--ink-soft);
  font-size: 0.97rem;
}

.page p {
  margin: 0 0 14px;
}

.page ul {
  margin: 0 0 14px;
  padding-left: 22px;
}

.page a {
  color: var(--ink);
}

.page-updated {
  font-size: 0.85rem;
  color: var(--ink-muted);
}

/* "Also called Cool Winter." — sits directly under a season page's h1. */
.also-called {
  margin: -10px 0 20px;
  font-size: 15.5px;
  color: var(--ink-soft);
}

/* ---- Seasons index ---- */

.season-group h2 {
  margin: 40px 0 14px;
  padding-top: 14px;
  border-top: 1px solid var(--line-light);
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: 26px;
  font-weight: 400;
  line-height: 1.2;
  color: var(--ink);
}

/* On a narrow screen the cards fit as many to a row as there is room for. */
.season-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 16px;
}

/* From the width where three fit, always exactly three: a group holds exactly
   three seasons, so they divide the row evenly and no empty slot is left at
   the end of it. */
@media (min-width: 700px) {
  .season-cards {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 24px;
  }
}

.season-card {
  display: block;
  padding: 16px;
  background: var(--card);
  border: 1px solid var(--line-light);
  border-radius: 2px;
  text-decoration: none;
  color: inherit;
}

/* The card used to rise off the page and drop a shadow when you pointed at it.
   Twelve cards doing that made the page move like a game board, so the only
   answer to the pointer now is the hairline going dark. */
.season-card:hover {
  border-color: var(--ink);
}

.season-card h3 {
  margin: 14px 0 2px;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: 20px;
  font-weight: 400;
  line-height: 1.2;
}

.season-card-traits {
  margin: 0;
  font-size: 0.8rem;
  color: var(--ink-soft);
}

/* ---- Fan deck (the palette as a printed fan) ---- */

/* One blade: white card stock with a colour family printed down it, pale at
   the tip and deep towards the gather, and blank stock the rest of the way
   down to the rivet. The blades are printed flat: no shadow under them and no
   ink on their edges. The white rule each one lays over its neighbour is what
   separates them. */
.fan-card {
  display: flex;
  flex-direction: column;
  gap: var(--rule); /* the card showing through, as the rules between cells */
  background: var(--card);
  overflow: hidden; /* so the printing stops at the blade's edge */
}

/* The cells share the printed run between them, so every blade prints from its
   tip down to the same line whether it carries two colours or three. That is
   what keeps the opened fan an even arc with no blank card showing anywhere
   along the inner or the outer edge. */
.fan-cell {
  flex: 1 1 0;
  min-height: 0;
}

/* ---- Shared fan geometry ----
   The index fan and the season-page fan are the same object at two sizes:
   blades threaded on a ring and swung open. Every blade is pinned to the same
   point and turns about the ring's centre — that one shared pivot, not the
   tilt, is what makes a fan rather than a row of leaning cards. Only the
   numbers below differ between the two, so they can never drift apart. */
.fan-deck,
.mini-fan {
  /* The ring sits behind the blades, so the two have to stack against each
     other and nothing else on the page. */
  position: relative;
  isolation: isolate;
  /* The pivot is the centre of the blade's half-round end, which is what makes
     every blade trace the same circle at the gather however far it is swung. */
  --pivot-up: calc(var(--blade-w) / 2);
}

.fan-deck .fan-card,
.mini-fan .fan-card {
  position: absolute;
  left: var(--rivet-x);
  bottom: var(--rivet-y);
  width: var(--blade-w);
  height: var(--blade-h);
  margin-left: calc(var(--blade-w) / -2); /* centres the blade on the ring */
  /* The same white margin all round the printing. Because the blades overlap,
     each one's side margin lays a white rule over its neighbour, which is
     where the fan's radial lines come from. */
  /* The printing runs on past the pivot and is clipped by the blade's round
     end, so the gather is a thin crescent of card rather than a white disc in
     the middle of the fan. */
  padding: var(--pad) var(--pad) var(--handle);
  border-radius: var(--tip-r) var(--tip-r) var(--pivot-up) var(--pivot-up);
  transform-origin: 50% calc(100% - var(--pivot-up));
  transform: rotate(var(--rot, 0deg));
}

/* The rivet the blades are threaded on: one flat dot in the page's own grey.
   It was a shiny brass stud, and the shine was the most toy-like thing on the
   page. Because every blade ends in the same half-round, their ends stack
   into one round gather and the rivet sits in the middle of it — punched
   through all of them at once, which is what makes it read as the thing
   holding the fan together. */
.fan-deck::after,
.mini-fan::after {
  content: "";
  position: absolute;
  left: var(--rivet-x);
  bottom: calc(var(--rivet-y) + var(--pivot-up) - var(--rivet) / 2);
  width: var(--rivet);
  height: var(--rivet);
  margin-left: calc(var(--rivet) / -2);
  background: var(--ink-soft);
  border-radius: 50%;
  z-index: 20; /* above every blade */
}

/* Full-size fan on season pages. Centred, because a fan opens about its ring
   and a centred one is the only placement that doesn't look like it slipped.
   The deck is copied from a paint-chip fan: long narrow blades, a short stack
   of small cells printed near the tip, and a good length of bare card below
   the printing for the hand. Those proportions -- a blade about four and a
   half times as long as it is wide, printed over the top sixty per cent -- are
   what make it read as a fan deck rather than a chart.
   Every blade is the same length and prints to the same line, so a blade of
   two colours simply divides that run into two rather than ending short. */
.fan-deck {
  --rivet-x: 50%;
  --rivet-y: 12px;
  --blade-w: 40px;
  --blade-h: 200px;
  --pad: 3px;
  --handle: 70px;
  --rule: 2px;
  --tip-r: 0px;
  --rivet: 14px;
  height: 214px;
  margin: 26px 0 0;
}

/* Narrow screens keep the fan — just smaller. Nothing is lost at this size:
   the fan carries no text, and the swatches below it name every colour. */
@media (max-width: 640px) {
  .fan-deck {
    --rivet-y: 6px;
    --blade-w: 24px;
    --blade-h: 122px;
    --pad: 2px;
    --handle: 43px;
    --rule: 1.2px;
    --tip-r: 0px;
    --rivet: 9px;
    height: 130px;
  }
}

/* Miniature fan on the seasons index cards — fixed size so the grid stays
   orderly. Set far enough in from the card's edge that the widest blade still
   clears it. */
.mini-fan {
  --rivet-x: 50%;
  --rivet-y: 4px;
  --blade-w: 15px;
  --blade-h: 78px;
  --pad: 1px;
  --handle: 27px;
  --rule: 0.8px;
  --tip-r: 0px;
  --rivet: 6px;
  height: 84px;
}

/* The cards are wide on a full screen, so the fan grows with them rather than
   sitting as a small mark in a lot of empty card. */
@media (min-width: 900px) {
  .mini-fan {
    --rivet-y: 7px;
    --blade-w: 26px;
    --blade-h: 138px;
    --pad: 2px;
    --handle: 48px;
    --rule: 1.4px;
    --tip-r: 0px;
    --rivet: 10px;
    height: 146px;
  }
}

/* ---- Season detail pages ---- */

.shop-season-link {
  margin: 16px 0 64px;
  font-size: 0.88rem;
}

.shop-season-link a {
  color: var(--ink);
  font-weight: 500;
  text-decoration: none;
  border-bottom: 1px solid var(--line);
  padding-bottom: 1px;
}

.season-prose {
  max-width: 62ch;
}

.page .section-label {
  margin: 48px 0 8px;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: 26px;
  font-weight: 400;
  line-height: 1.2;
  color: var(--ink);
}

.color-family {
  margin: 0 0 64px;
}

.color-family:last-child {
  margin-bottom: 44px;
}

.family-label {
  margin: 26px 0 20px;
  padding-bottom: 9px;
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
  border-bottom: 1px solid var(--line-light);
}

.family-swatches {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
  gap: 30px 28px;
}

/* A hand-assembled feel: barely-there tilt on some samples. */
.family-swatches .swatch:nth-child(3n) {
  transform: rotate(0.6deg);
}

.family-swatches .swatch:nth-child(5n) {
  transform: rotate(-0.4deg);
}

/* Fabric sample: hairline backing layer with the color cloth inset 1px,
   so the edge (including the pinked cut) reads as a thin fabric edge. */
.fabric {
  position: relative;
  aspect-ratio: 14 / 17;
  background: var(--line);
}

.fabric-color {
  position: absolute;
  inset: 1px;
}

/* Woven texture: page-level SVG turbulence filter, blended very quietly. */
.fabric-weave {
  position: absolute;
  inset: 0;
  filter: url(#weave-texture);
  mix-blend-mode: soft-light;
  opacity: 0.12;
  pointer-events: none;
}

/* Punched hole with a muted brass ring — the one decorative element. */
.fabric-hole {
  position: absolute;
  top: 10px;
  left: 10px;
  width: 10px;
  height: 10px;
  box-sizing: content-box;
  border: 1.5px solid #b79a63;
  border-radius: 50%;
  background: var(--paper);
}

.texture-defs {
  position: absolute;
}

.swatch-name {
  display: block;
  margin: 8px 0 0;
  font-size: 0.78rem;
  color: var(--ink);
  line-height: 1.3;
}

@media (max-width: 560px) {
  .family-swatches {
    grid-template-columns: repeat(3, 1fr);
    gap: 20px 12px;
  }
}

/* Written as .page a so it beats the plain "links on a reading page are ink
   coloured" rule above. Without that this button was ink lettering on an ink
   ground — a solid black slab you could not read. */
.page a.shop-season-btn {
  display: inline-block;
  padding: 13px 30px;
  font-size: 0.95rem;
  font-weight: 500;
  color: #fff;
  background: var(--ink);
  border-radius: var(--radius);
  text-decoration: none;
  transition: background 0.15s;
}

.page a.shop-season-btn:hover {
  background: #0f0f0f;
}

.sister-seasons {
  margin-top: 18px;
  font-size: 0.9rem;
}

/* ---- Account (sign-in control in the header, and its dialog) ---- */

/* Quiet by design: signing in is optional, so the control sits beside the nav
   at nav weight rather than competing with it. */
/* The person icon, built like `.header-icon` beside it — an icon in a row of
   icons, not a button parked next to them. */
.account-btn {
  position: relative;
  display: flex;
  align-items: center;
  border: none;
  background: none;
  padding: 2px 0;
  color: var(--ink-soft);
  cursor: pointer;
  transition: color 0.15s;
}

.account-btn svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
}

.account-btn:hover {
  color: var(--ink);
}

/* Signed in, the head fills the way a saved heart does, so a glance at the bar
   tells you the site knows you without anything having to be spelled out. */
.account-btn.signed-in {
  color: var(--ink);
}

.account-btn.signed-in svg path:first-child {
  fill: currentColor;
}

.account-btn:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 3px;
}

/* Same dimming and same fade as the item detail view, so the site only ever
   has one kind of dialog. Above the cookie bar (60) rather than level with it:
   a first-time visitor can meet both at once, and the one they just opened is
   the one that should be on top. */
.account-overlay {
  display: flex;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 70;
  align-items: center;
  justify-content: center;
  padding: 20px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.15s ease, visibility 0s linear 0.15s;
}

.account-overlay.visible {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity 0.15s ease, visibility 0s;
}

.account-card {
  position: relative;
  width: 100%;
  max-width: 380px;
  padding: 26px 24px 24px;
  border-radius: var(--radius);
  background: var(--card);
  text-align: left;
}

/* The heading and the lines of text around the form sit in the middle, the way
   most sign-in boxes do. The form itself (labels, boxes) stays left-aligned,
   because that is how people read a form. */
.account-card h2 {
  margin: 0 34px 10px; /* clear of the close button, on both sides so it stays centred */
  text-align: center;
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 500;
  letter-spacing: -0.01em;
}

.account-note {
  margin: 0 0 14px;
  text-align: center;
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.account-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.account-label {
  font-size: 12px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
}

.account-input {
  width: 100%;
  padding: 11px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  font: inherit;
  font-size: 1rem; /* 16px keeps iOS from zooming the page on focus */
  color: var(--ink);
}

.account-input:focus {
  outline: none;
  border-color: var(--ink);
}

.account-submit {
  margin-top: 4px;
  padding: 12px;
  border: none;
  border-radius: var(--radius);
  background: var(--ink);
  font: inherit;
  font-size: 0.95rem;
  font-weight: 500;
  color: #fff;
  cursor: pointer;
  transition: background 0.15s;
}

.account-submit:hover {
  background: #0f0f0f;
}

.account-submit:disabled {
  background: var(--line-light);
  color: var(--ink-muted);
  cursor: default;
}

/* Doubles as the "check your email" line once the link has been sent, which is
   why it is ink rather than muted — it is the answer, not a footnote. */
.account-status {
  margin: 2px 0 0;
  text-align: center;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--ink);
}

.account-status.error {
  color: #a33;
}

/* "or" with a line running out to each side, the same space above and below. */
.account-or {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 16px 0;
  font-size: 0.8rem;
  color: var(--ink-muted);
}

.account-or::before,
.account-or::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--line);
}

/* The status line under Sign in is empty until something goes wrong, and an
   empty line still adds a gap. Take it out so "or" sits evenly. */
.account-status:empty {
  display: none;
}

.account-alt {
  width: 100%;
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  font: inherit;
  font-size: 0.95rem;
  color: var(--ink);
  cursor: pointer;
}

.account-alt:hover {
  border-color: var(--ink-soft);
}

/* In the signed-in panel these sit straight in the card rather than as rows of
   a form, so they have to be given the width and the gaps the form's flex
   column would otherwise have handed them. */
.account-card > .account-submit {
  display: block;
  width: 100%;
}

/* The two answers to the palette question, side by side and the same size, so
   neither one looks like the one we would rather they picked. Long names wrap
   onto their own row instead of squeezing. */
.account-choices {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 12px;
}

.account-choices .account-submit {
  flex: 1 1 140px;
  margin-top: 0;
}

.account-submit + .account-alt {
  margin-top: 10px;
}

.account-submit + .account-form {
  margin-top: 14px;
}

.account-alt + .account-alt {
  margin-top: 8px;
}

/* Inside a form the form's own gap already spaces the buttons. */
.account-form > .account-alt {
  margin-top: 0;
}

/* A line under a button explains the button, so it sits centred beneath it. */
.account-alt + .account-hint {
  margin-top: 8px;
  text-align: center;
}

/* Under the password box, on the right, where most sites keep it. */
.account-forgot {
  margin: 2px 0 0;
  font-size: 0.8rem;
  text-align: right;
  color: var(--ink-soft);
}

/* A label, its box, and anything that needs saying underneath — kept together
   so the form's own gap spaces the questions, not the parts of one question. */
.account-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* The Show/Hide button sits inside the box rather than beside it, so the box
   stays the full width and the row does not grow a second column. */
.account-box {
  position: relative;
  display: flex;
}

.account-input.has-peek {
  padding-right: 62px;
}

.account-peek {
  position: absolute;
  top: 1px;
  right: 1px;
  bottom: 1px;
  padding: 0 12px;
  border: none;
  border-radius: 0 var(--radius) var(--radius) 0;
  background: transparent;
  font: inherit;
  font-size: 0.8rem;
  color: var(--ink-soft);
  cursor: pointer;
}

.account-peek:hover {
  color: var(--ink);
}

.account-hint {
  margin: 4px 0 0;
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--ink-muted);
}

/* Reads as a link because it goes somewhere, in the small way this dialog has
   of going somewhere: it swaps the form for another one. */
.account-link {
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  font-size: inherit;
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.account-link:hover {
  color: var(--ink-soft);
}

.account-foot {
  margin: 20px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--line-light);
  font-size: 0.85rem;
  text-align: center;
  color: var(--ink-soft);
}

/* Under Sign out, at the very bottom of the account panel. Smaller and muted:
   it is the one thing in here nobody is looking for, and it should not draw a
   thumb on its way past. */
.account-delete-line {
  margin: 14px 0 0;
  font-size: 0.8rem;
}

.account-delete-line .account-link {
  color: var(--ink-muted);
}

.account-delete-line .account-link:hover {
  color: #a33;
}

/* The only button on the site that takes something away for good, so it is the
   only one that isn't ink. It stays grey and unpressable until the word has been
   typed, which is the confirmation. */
.account-submit.account-danger {
  background: #a33;
}

.account-submit.account-danger:hover {
  background: #8c2b2b;
}

.account-submit.account-danger:disabled {
  background: var(--line-light);
  color: var(--ink-muted);
  cursor: default;
}

/* Top-right here rather than top-left: the detail view's close button sits
   over a photo, this one sits over a heading. */
.account-close {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 34px;
  height: 34px;
  border: none;
  border-radius: 50%;
  background: transparent;
  font-size: 1rem;
  line-height: 1;
  color: var(--ink-soft);
  cursor: pointer;
}

.account-close:hover {
  background: var(--paper);
  color: var(--ink);
}

@media (prefers-reduced-motion: reduce) {
  .account-overlay {
    transition: none;
  }
}

@media (max-width: 640px) {
  .account-card {
    padding: 24px 20px 20px;
  }
}

/* ---- Find your season (the quiz panel on the seasons index) ---- */

/* Under the h1, above the introduction. White with the same hairline edge as
   the season cards and no shadow, and the full width of the page, with what
   is inside centered. find-your-season.js swaps what is inside it. */
.find-season {
  margin: 0 0 32px;
  padding: 20px;
  background: var(--card);
  border: 1px solid var(--line-light);
  border-radius: 2px;
}

.page .find-season-title {
  margin: 0 0 6px;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: clamp(22px, 2.6vw, 26px);
  font-weight: 400;
  line-height: 1.2;
  color: var(--ink);
}

/* While she is answering, the question is the heading and the card's own
   title would only repeat itself above all seven. Taken off the page rather
   than removed: it is what names the panel to a screen reader. */
.find-season-asking .find-season-title {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The title and each question are focused when they change, for screen
   readers; they are not controls, so no ring. */
.find-season [tabindex="-1"]:focus {
  outline: none;
}

.page .find-season-lede {
  margin: 0 0 16px;
}

/* Near-black, like Confirm in the palette pop-up. */
.find-season-start {
  padding: 12px 34px;
  font-family: inherit;
  font-size: 0.95rem;
  font-weight: 500;
  color: #fff;
  background: var(--ink);
  border: none;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background 0.15s;
}

.find-season-start:hover {
  background: #0f0f0f;
}

.find-season-start:focus-visible,
.find-season-option:focus-visible,
.find-season-chip:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

.page .find-season-count {
  margin: 0 0 4px;
  font-size: 0.8rem;
  color: var(--ink-muted);
}

/* The heading goes inline so the "?" sits right after the last word of the
   question rather than off at the edge of the card. It is the anchor the
   explanation hangs from, so it is positioned. */
.find-season-head {
  position: relative;
  margin: 0;
}

.page .find-season-question {
  display: inline;
  margin: 0;
  font-size: 1.15rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--ink);
}

/* A plain line after the question, not a bubble. A "?" in a dark circle was a
   badge, and the badge was the loudest thing on the card; as four small grey
   words it says what it opens and waits to be noticed. */
.find-season-why-btn {
  margin-left: 7px;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.8rem;
  font-weight: 400;
  line-height: 1;
  color: var(--ink-muted);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.find-season-why-btn:hover,
.find-season-why-btn.is-open {
  color: var(--ink);
}

/* It lies over the steps rather than opening a gap in them, so nothing on the
   screen moves when it appears. A plain sheet with a hairline round it: as a
   dark rounded card under a drop shadow it was a speech bubble. */
.page .find-season-why {
  position: absolute;
  z-index: 5;
  top: calc(100% + 7px);
  left: 0;
  right: 0;
  margin: 0;
  padding: 10px 12px;
  border: 1px solid var(--ink);
  background: var(--card);
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--ink);
}

/* What to do, in order. Tight: these sit on a phone above six answers. */
.page .find-season-steps {
  margin: 6px 0 0;
  padding-left: 18px;
  font-size: 0.86rem;
  line-height: 1.4;
}

.page .find-season-steps li + li {
  margin-top: 4px;
}

/* One answer to a row, always. Two to a row made short answers into wide
   rounded tiles and the page into a board of buttons; a list read straight
   down is how a question with answers looks on paper. */
.find-season-options {
  display: grid;
  gap: 6px;
  margin-top: 14px;
}

/* A plain row: square corners, a hairline round it, and a small empty circle
   at the left that fills when the row is the answer given. */
.find-season-option {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 11px 13px;
  text-align: left;
  font-family: inherit;
  font-size: 0.93rem;
  line-height: 1.35;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 0;
  cursor: pointer;
}

.find-season-option::before {
  content: "";
  flex: none;
  width: 9px;
  height: 9px;
  margin-top: 0.3em;
  border: 1px solid var(--ink-muted);
  border-radius: 50%;
}

.find-season-option:hover {
  border-color: var(--ink);
}

.find-season-unsure {
  color: var(--ink-soft);
}

.find-season-option[aria-pressed="true"] {
  border-color: var(--ink);
}

.find-season-option[aria-pressed="true"]::before {
  border-color: var(--ink);
  background: var(--ink);
}

/* The hair and eye questions are answered from small drawings — a lock of
   hair, an iris — with the answer written underneath. The row wraps, so three
   fit across a phone and more on a wider screen. */
.page .find-season-chip-group {
  margin: 14px 0 6px;
  font-size: 0.78rem;
  color: var(--ink-muted);
}

.find-season-chips {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
  gap: 10px;
}

/* A column, so a two-line answer next to a three-line one still leaves the
   swatches lined up along the top of the row. */
.find-season-chip {
  display: flex;
  flex-direction: column;
  padding: 0;
  font-family: inherit;
  text-align: center;
  color: var(--ink);
  background: none;
  border: 0;
  cursor: pointer;
}

.find-season-chip-art {
  display: block;
  padding: 4px;
  border-radius: 0;
}

.find-season-art {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 5 / 4;
  overflow: visible;
}

.find-season-chip-label {
  display: block;
  margin-top: 6px;
  font-size: 0.78rem;
  line-height: 1.3;
}

.find-season-chip:hover .find-season-chip-art {
  background: var(--paper);
  box-shadow: inset 0 0 0 1px var(--line);
}

.find-season-chip[aria-pressed="true"] .find-season-chip-art {
  background: var(--paper);
  box-shadow: inset 0 0 0 2px var(--ink);
}

/* The hair swatch, drawn like the sample on a box of dye: the answer's color
   edge to edge, thin strands a step lighter and a step darker over it so it
   reads as texture rather than a flat fill, a shadow gathering along the
   bottom edge and a sheen across the upper third. How strong the sheen is
   comes from the answer's own gloss figure, so the ash tones stay flat and
   the golden, chestnut and copper ones shine. Square, like the swatches on
   the season pages, and clipped to that edge so the strands run all the way
   out to it. The swatch is a little wider than it is tall so all seven
   answers fit a phone screen without scrolling; the drawing is cropped top and
   bottom to that shape rather than squashed into it. */
.find-season-art-hair {
  aspect-ratio: 6 / 5;
  overflow: hidden;
}

.art-hair-ground {
  fill: var(--chip-base);
}

.art-hair-strand,
.art-hair-glint,
.art-hair-lock,
.art-hair-band {
  fill: none;
}

/* Strands that start partway down end in a tip rather than a cut edge. */
.art-hair-strand,
.art-hair-glint {
  stroke-linecap: round;
}

/* The crease between one lock of hair and the next. */
.art-hair-lock {
  stroke: var(--chip-dark);
}

/* The strands the sheen actually lands on. */
.art-hair-glint {
  stroke: var(--chip-light);
}

.art-hair-strand-light {
  stroke: var(--chip-light);
}

.art-hair-strand-dark {
  stroke: var(--chip-dark);
}

.art-hair-glint-light {
  stroke: var(--chip-light);
}

.art-hair-depth .art-hair-band {
  stroke: var(--chip-dark);
}

/* Glossy hair takes a stronger sheen than ashy hair, which is what the
   answer's own gloss figure sets. */
.art-hair-sheen {
  opacity: var(--chip-gloss, 0.6);
}

.art-hair-sheen .art-hair-band {
  stroke: var(--chip-light);
}

/* An iris: the rim color over the whole circle, the color near the pupil
   blurred into it, fibers spoking out, a darker ring at the edge, then the
   pupil and its glint on top. */
.art-iris-rim {
  fill: var(--chip-rim);
}

.art-iris-center {
  fill: var(--chip-center);
  filter: blur(5px);
}

.art-iris-fiber {
  fill: none;
}

.art-iris-fiber-dark {
  stroke: #000;
  stroke-width: 0.8;
  opacity: 0.2;
}

.art-iris-fiber-light {
  stroke: #fff;
  stroke-width: 0.7;
  opacity: 0.17;
}

/* The ring the fibers gather into around the pupil. */
.art-iris-collar {
  fill: none;
  stroke: #000;
  stroke-width: 3;
  opacity: 0.18;
  filter: blur(2px);
}

.art-iris-edge {
  fill: none;
  stroke: #000;
  stroke-width: 7;
  opacity: 0.45;
  filter: blur(2px);
}

.art-iris-pupil {
  fill: #16110e;
}

.art-iris-glint {
  fill: #fff;
  opacity: 0.85;
}

.art-iris-glint-small {
  opacity: 0.4;
}

.find-season-chip[aria-pressed="true"] .find-season-chip-label {
  font-weight: 600;
}

/* The ring and the cloth. Both are drawn from the answer's own three colors,
   which arrive as --chip-base, --chip-light and --chip-dark; the "they look
   the same to me" answer sends a second set and the drawing repeats itself
   over its right-hand half in those. */
.art-ring-band {
  fill: none;
  stroke: var(--chip-base);
  stroke-width: 11;
}

.art-ring-sheen,
.art-ring-shade {
  fill: none;
  stroke-width: 5;
  stroke-linecap: round;
}

.art-ring-sheen {
  stroke: var(--chip-light);
  opacity: 0.85;
}

.art-ring-shade {
  stroke: var(--chip-dark);
  opacity: 0.6;
}

.art-ring-hole {
  fill: var(--card);
  stroke: rgba(0, 0, 0, 0.12);
  stroke-width: 0.8;
}

.art-cloth-ground {
  fill: var(--chip-base);
}

.art-cloth-fold {
  fill: none;
  stroke-linecap: round;
}

.art-cloth-fold-light {
  stroke: var(--chip-light);
  stroke-width: 9;
  opacity: 0.55;
}

.art-cloth-fold-dark {
  stroke: var(--chip-dark);
  stroke-width: 11;
  opacity: 0.35;
}

/* Bright white on a white card would otherwise be an invisible swatch. */
.art-cloth-edge {
  fill: none;
  stroke: rgba(0, 0, 0, 0.16);
  stroke-width: 1;
}

/* "Not sure" after the swatches. It is the way out, not a fourth answer, so it
   is only as wide as its own word: full width it was the biggest thing on the
   card and read as the thing to press. */
.find-season-unsure-row {
  display: flex;
  margin-top: 12px;
}

.find-season-unsure-row .find-season-option {
  padding: 8px 14px;
  font-size: 0.86rem;
}

.find-season-unsure-row .find-season-option::before {
  display: none;
}

/* The instructions, plain on the page. They used to sit in a tinted rounded
   box under a "WHAT TO DO" label, which made a card inside a card and put a
   second heading over three short lines. The lines say what to do by
   themselves, so the box and that one label are gone; the label over the
   worked example below them stays, because that part does need naming. */
.page .find-season-howto {
  margin: 9px 0 0;
  padding: 0;
}

.page .find-season-howto > :first-child {
  margin-top: 0;
}

.page .find-season-howto > .find-season-lead:first-child {
  display: none;
}

/* What to look for while you hold the two colors up. Part of the same block as
   the steps, a line under them rather than a panel of its own. */
.find-season-demo-holder {
  margin-top: 7px;
}

/* The label over each of the two halves: "What to do" over the numbered steps,
   "What you are looking for" over the two lines. Set as small capitals, which
   is how a label reads as a label rather than as a short sentence someone
   wrote in grey. */
.page .find-season-lead {
  margin: 7px 0 3px;
  font-size: 0.71rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* The site puts a left pad on every ul, which pushed these two lines in from
   the margin everything else on the card sits on. */
.page .find-season-demo {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Two plain lines, no rule down the side. The rule was there to group them,
   but a vertical line beside two lines of text reads as a quote or a callout,
   and there was nothing to quote. "Better:" and "Worse:" group them by
   themselves. */
.find-season-demo-row {
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--ink-muted);
}

/* Each is three lines on a phone now that the reading sits on its own line, so
   they need a gap to stay two blocks rather than one paragraph. */
.find-season-demo-row + .find-season-demo-row {
  margin-top: 6px;
}

.find-season-demo-head {
  font-weight: 600;
  color: var(--ink);
}

/* The four words that are the whole test. The signs before them are the
   evidence; this is the reading, so it comes out of the muted grey into full
   ink and is the part your eye lands on. It gets its own line with an arrow in
   front: run on at the end of the sentence, in bold, it read as more of the
   sentence rather than as what the sentence adds up to. */
.find-season-demo-tell {
  display: block;
  font-weight: 600;
  color: var(--ink);
}

.find-season-demo-tell::before {
  content: "\2192";
  margin-right: 5px;
  color: var(--ink-muted);
  font-weight: 400;
}

.find-season-nav {
  margin-top: 14px;
}

.find-season .text-btn {
  font-size: 0.88rem;
}

/* Results: stacked with a hairline between them on a phone, side by side in
   equal columns from where they fit. */
.find-season-results {
  display: grid;
  gap: 18px;
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 700px) {
  .find-season-results {
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 24px;
  }
}

/* The two results sit in columns of the same width, so the two "Shop" buttons
   ought to be the same width as each other and on the same line. Left alone
   they were neither: a longer season name wrapped the link and the button onto
   two lines in one column and not in the other, and a longer reason pushed one
   button further down than its neighbour. So each card is a column with its
   links pinned to the foot, and the link and the button each take a line of
   their own however long the name runs. */
.find-season-result {
  display: flex;
  flex-direction: column;
  padding-top: 16px;
  border-top: 1px solid var(--line-light);
}

.page .find-season-name {
  margin: 0 0 10px;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: 20px;
  font-weight: 400;
  line-height: 1.2;
  color: var(--ink);
}

/* The five signature colors, with the same hairline the pop-up's strips use
   so a very light color still reads as a segment. */
.find-season-strip {
  display: flex;
  max-width: 280px;
  height: 28px;
  border-radius: 4px;
  overflow: hidden;
}

.find-season-strip span {
  flex: 1;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.06);
}

.page .find-season-reason {
  margin: 10px 0 0;
  font-size: 0.9rem;
}

/* "Read about" reads as the end of the line above, so it sits close under it.
   The Shop button keeps a clear gap of its own and stays pinned to the foot,
   so the two buttons still line up when one reason runs longer. */
.page .find-season-readline {
  margin: 4px 0 0;
}

.page .find-season-links {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  margin: auto 0 0;
  padding-top: 18px;
}

/* Underlined the way "Shop True Winter" is under a season page's fan. */
.page a.find-season-read {
  align-self: flex-start;
  font-size: 0.88rem;
  font-weight: 500;
  text-decoration: none;
  border-bottom: 1px solid var(--line);
  padding-bottom: 1px;
}

/* Fills its column, but never grows past the color strip above it: when the
   quiz lands on a single season there is only one column, and a button the
   whole width of the card is a slab of black rather than a button. Capped
   here, it lines up with the strip whether there is one result or three. */
.page .find-season-links a.shop-season-btn {
  display: block;
  max-width: 280px;
  padding: 10px 20px;
  font-size: 0.88rem;
  text-align: center;
}

/* One season on its own: a 280px strip and button hugging the left of the
   whole panel looked off-center, so the card is centered instead. The strip
   and button are given their width back, since centering would shrink them. */
.find-season-results.is-single .find-season-result {
  align-items: center;
  text-align: center;
}

.find-season-results.is-single .find-season-strip,
.page .find-season-results.is-single .find-season-links {
  width: 100%;
  max-width: 280px;
}

.page .find-season-note {
  margin: 18px 0 0;
  font-size: 0.88rem;
}

/* One button for every season on the screen, when the quiz lands on more than
   one. Centered so it reads as belonging to the whole list rather than to the
   card it sits nearest, and capped like the buttons inside the cards. */
.page .find-season-both {
  margin: 12px 0 0;
  text-align: center;
}

.page .find-season-both a.shop-season-btn {
  display: inline-block;
  max-width: 280px;
  padding: 10px 20px;
  font-size: 0.88rem;
  text-align: center;
}

.find-season-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 22px;
  margin-top: 14px;
}

/* The invitation: everything centered, the photo as the one big button and
   the questions as a quiet link under it. The heading sits outside this
   block, so it is centered from here too. */
.find-season-invite {
  padding: 12px 0 8px;
  text-align: center;
}

.page .find-season:has(> .find-season-body > .find-season-invite) .find-season-title {
  text-align: center;
}

/* In the open dialog the heading keeps clear of the × on the right. Here it
   keeps the same space on the left too, or it would sit off-center. */
.page .find-season-dialog:has(> .find-season-body > .find-season-invite) .find-season-title {
  padding-left: 32px;
}

.page .find-season-invite .find-season-lede {
  max-width: 30rem;
  margin: 0 auto 20px;
  font-size: 1.05rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

/* The chart of the twelve seasons, in two halves. Only with the invitation
   on the page: not over a question or a result, and not in the open dialog,
   where "Start over" brings the invitation back without it. The columns
   have a set size before their colors arrive, so the box doesn't change
   height when they do.

   On a phone the two halves sit side by side above the heading. */
.find-season {
  --swatch: 18px;
  --swatch-gap: 3px;
  --group-gap: calc(var(--swatch) * 0.7);
}

.find-season-chart-side {
  display: none;
}

.find-season:not(.find-season-dialog):has(> .find-season-body > .find-season-invite) {
  display: grid;
  grid-template-columns: auto auto;
  grid-template-areas:
    "left right"
    "title title"
    "body body";
  justify-content: center;
  column-gap: var(--group-gap);
}

.find-season:not(.find-season-dialog):has(> .find-season-body > .find-season-invite) > .find-season-chart-side {
  display: flex;
  gap: var(--group-gap);
  margin: 8px 0 18px;
}

/* Each half pushed toward the other, so they read as one chart even when the
   words below are wider than it. */
.find-season-chart-left { grid-area: left; justify-self: end; }
.find-season-chart-right { grid-area: right; justify-self: start; }

.find-season:not(.find-season-dialog):has(> .find-season-body > .find-season-invite) > .find-season-title {
  grid-area: title;
}

.find-season:not(.find-season-dialog):has(> .find-season-body > .find-season-invite) > .find-season-body {
  grid-area: body;
}

.find-season-chart-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.find-season-chart-cols {
  display: flex;
  gap: var(--swatch-gap);
  width: calc(var(--swatch) * 3 + var(--swatch-gap) * 2);
  height: calc(var(--swatch) * 5 + var(--swatch-gap) * 4);
}

.find-season-chart-col {
  display: flex;
  flex-direction: column;
  gap: var(--swatch-gap);
}

/* A hairline inside each square so the palest colors still have an edge on
   the white card. */
.find-season-chart-col span {
  display: block;
  width: var(--swatch);
  height: var(--swatch);
  border-radius: 2px;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.06);
}

/* Each group's name under its three columns, on a hairline, like the axis of
   a chart. */
.find-season-chart-group > span {
  padding-top: 6px;
  border-top: 1px solid var(--line);
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* Wide enough for the middle and both sides: the invitation stays centered
   and the halves sit either side of it, instead of it being a narrow strip
   with blank card left and right. */
@media (min-width: 1000px) {
  .find-season {
    --swatch: 22px;
  }

  .find-season:not(.find-season-dialog):has(> .find-season-body > .find-season-invite) {
    grid-template-columns: 1fr minmax(0, 32rem) 1fr;
    grid-template-areas:
      "left title right"
      "left body right";
    justify-content: stretch;
    column-gap: 32px;
    padding: 28px 32px;
  }

  .find-season:not(.find-season-dialog):has(> .find-season-body > .find-season-invite) > .find-season-chart-side {
    grid-row: 1 / 3;
    align-self: center;
    justify-self: center;
    margin: 0;
  }
}

@media (min-width: 1200px) {
  .find-season {
    --swatch: 28px;
  }
}

.page .find-season-way-note {
  margin: 8px 0 0;
  font-size: 0.85rem;
  color: var(--ink-muted);
}

.find-season-invite .find-season-start {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-width: min(280px, 100%);
  padding: 15px 36px;
  font-size: 1rem;
}

.find-season-camera {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linejoin: round;
}

.page .find-season-or {
  margin: 22px 0 0;
  font-size: 0.92rem;
  color: var(--ink-soft);
}

/* A link to look at, a button underneath, since it opens the questions
   rather than going to another page. */
.find-season-questions-start {
  padding: 0;
  font: inherit;
  font-weight: 500;
  color: var(--ink);
  background: none;
  border: none;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.find-season-questions-start:hover {
  text-decoration-thickness: 2px;
}

.find-season-questions-start:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 2px;
}

/* The time goes on a line of its own under the link. */
.page .find-season-or .find-season-way-note {
  display: block;
  margin-top: 4px;
}

/* On the questions that hold two colors up to the face: pick what suits you,
   not what you like. It sat on a rounded beige panel, which made a card inside
   the card; the bold half of the line is enough to stop the eye. */
.page .find-season-remind {
  margin: 8px 0 12px;
  font-size: 0.92rem;
  line-height: 1.45;
}

/* After a result from one way, the other. */
.find-season-more {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 18px;
  margin-top: 18px;
  padding: 14px 16px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.page .find-season-more p {
  flex: 1 1 260px;
  margin: 0;
  font-size: 0.92rem;
}

.find-season-more .find-season-start {
  padding: 10px 22px;
}

.find-season-checks {
  list-style: none;
  margin: 0 0 18px;
  padding: 0;
  max-width: 34rem;
}

.find-season-checks li + li {
  margin-top: 8px;
}

.find-season-check {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  font-size: 0.95rem;
  line-height: 1.45;
  cursor: pointer;
}

.find-season-check input {
  flex: none;
  width: 18px;
  height: 18px;
  margin: 2px 0 0;
  accent-color: var(--ink);
}

/* "Most important", "Helps a lot", "Required": small labels over each list. */
.page .find-season-check-heading {
  margin: 0 0 8px;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* The white-or-grey question reads as its own question, not a fourth list. */
.find-season-reference {
  margin: 0 0 18px;
  padding: 0;
  border: 0;
  min-width: 0;
}

.find-season-reference legend {
  padding: 0;
}

.page .find-season-reference-why {
  margin: -6px 0 10px;
  font-size: 0.85rem;
  color: var(--ink-soft);
}

/* Four short answers: two across wherever they fit, so the block is two rows
   instead of four. */
.find-season-reference .find-season-checks {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 8px 16px;
  margin: 0;
}

.find-season-reference .find-season-checks li + li {
  margin-top: 0;
}

/* The two required lines are a different kind of thing from the photo advice
   above them: a boxed panel of their own, not one more list. */
.find-season-consents {
  margin: 4px 0 18px;
  padding: 14px 16px 2px;
  max-width: 34rem;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.find-season-consents .find-season-checks {
  margin-bottom: 14px;
}

/* How sure the photo can be, updated as she ticks. Three bars for the three
   readings; the fourth case isn't a lower score but a warning, so it trades
   the bars for an amber panel. */
.find-season-confidence {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 10px;
  margin: 0 0 14px;
  max-width: 34rem;
  font-size: 0.92rem;
  font-weight: 500;
  line-height: 1.45;
}

.find-season-confidence-meter {
  display: inline-flex;
  gap: 3px;
}

.find-season-confidence-meter span {
  width: 16px;
  height: 6px;
  border-radius: 3px;
  background: var(--line);
}

.find-season-confidence-meter .is-on {
  background: var(--ink);
}

.find-season-confidence.is-limited {
  padding: 10px 14px;
  color: #6b4a06;
  background: #fdf4e1;
  border: 1px solid #ecc97a;
  border-radius: var(--radius);
}

.find-season-confidence-nudge {
  flex-basis: 100%;
  font-size: 0.82rem;
  font-weight: 400;
  color: var(--ink-soft);
}

.find-season-confidence.is-limited .find-season-confidence-nudge {
  color: inherit;
}

.page .find-season-needed {
  margin: 10px 0 0;
  font-size: 0.82rem;
  color: var(--ink-soft);
}

.find-season-photo-field {
  margin-bottom: 18px;
}

.page .find-season-photo-ask {
  margin: 0 0 10px;
  font-weight: 500;
}

/* A yes/no question and its two short answers share a line, even on a phone,
   instead of taking three rows. */
.find-season-yes-no {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  margin-bottom: 14px;
}

.page .find-season-yes-no .find-season-photo-ask {
  flex: 1 1 14rem;
  margin: 0;
}

.find-season-yes-no .find-season-options {
  grid-template-columns: 5.5rem 5.5rem;
  margin: 0;
}

.find-season-yes-no .find-season-option {
  padding: 8px 14px;
  justify-content: center;
  text-align: center;
}

/* Yes and No are a pair, not a list of answers to work down, so they keep
   their two columns and take no dot. */
.find-season-yes-no .find-season-option::before,
.find-season-tap-confirm .find-season-option::before {
  display: none;
}

.find-season-photo-rules {
  list-style: none;
  margin: 0 0 18px;
  padding: 0;
  max-width: 34rem;
  font-size: 0.95rem;
  line-height: 1.45;
}

.find-season-photo-rules li + li {
  margin-top: 8px;
}

/* Her photo, to tap the card or paper in. Kept short enough that the buttons
   under it are still in reach. */
.find-season-tap {
  margin: 0 0 12px;
  text-align: center;
}

.find-season-tap-photo {
  display: inline-block;
  max-width: 100%;
  max-height: 60vh;
  width: auto;
  height: auto;
  border-radius: 6px;
  cursor: crosshair;
  touch-action: manipulation;
}

.page .find-season-tap-message {
  min-height: 1.4em;
  margin: 0 0 6px;
  font-size: 0.9rem;
}

/* The tapped spot, blown up, beside the question of whether it's the card.
   Drawn with hard pixels so it reads as a close-up of her photo, not a
   color chip we made. */
.find-season-tap-confirm {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 18px;
  margin: 0 0 12px;
}

.find-season-tap-confirm[hidden] {
  display: none;
}

.find-season-tap-swatch {
  flex: none;
  width: 96px;
  height: 96px;
  border: 1px solid var(--line);
  border-radius: 6px;
  image-rendering: pixelated;
}

.find-season-tap-confirm-text {
  flex: 1 1 16rem;
}

.page .find-season-tap-ask {
  margin: 0;
  font-weight: 500;
}

.find-season-tap-confirm .find-season-options {
  margin-top: 10px;
}

@media (min-width: 560px) {
  .find-season-tap-confirm .find-season-options {
    grid-template-columns: 1fr 1fr;
  }
}

/* The file input is real and focusable, just not drawn: the button beside it
   is what she sees. */
.find-season-file {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.find-season-start[disabled] {
  opacity: 0.4;
  cursor: default;
}

/* ===== The quiz opens over the page =====

   The card on the seasons page is only the invitation and never changes
   height. Once she starts, find-your-season.js adds .find-season-dialog to
   that same card and puts a backdrop behind it, so the long screens -- the
   twelve questions, the photo checklist, the result -- scroll inside the
   dialog instead of growing the page and pushing the seasons down. The panel
   keeps its place in the page so every ".page .find-season-*" rule above
   still reaches what is inside it. */
.find-season-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(0, 0, 0, 0.45);
  animation: find-season-fade 0.15s ease-out;
}

@keyframes find-season-fade {
  from { opacity: 0; }
}

/* Overrides the page card: pinned over the middle of the screen, its own
   scroll when a screen runs long, and lifted off the page. */
.find-season.find-season-dialog {
  position: fixed;
  z-index: 1001;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(760px, calc(100vw - 40px));
  max-width: none;
  margin: 0;
  max-height: calc(100dvh - 40px);
  overflow-y: auto;
  overscroll-behavior: contain;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.22);
}

/* First child of the panel, so it stays at the top of the dialog as the
   screen scrolls. It takes no line of its own: on a line to itself it left a
   gap above the heading, so the heading comes up level with it instead and
   keeps clear of it on the right. */
.find-season-close {
  position: sticky;
  top: -4px;
  z-index: 2;
  display: block;
  margin: -6px -6px -26px auto;
  width: 32px;
  height: 32px;
  font-family: inherit;
  font-size: 22px;
  line-height: 1;
  color: var(--ink-muted);
  background: var(--card);
  border: none;
  border-radius: 50%;
  cursor: pointer;
}

.page .find-season-dialog .find-season-title {
  padding-right: 32px;
}

.find-season-close:hover {
  color: var(--ink);
}

.find-season-close:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* The page behind is held still while the dialog is open. */
body.find-season-held {
  overflow: hidden;
}

@media (max-width: 560px) {
  .find-season.find-season-dialog {
    padding: 16px;
    width: calc(100vw - 24px);
    max-height: calc(100dvh - 24px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .find-season-backdrop {
    animation: none;
  }
}
