/* Molecular Miracles — the shop, wherever it appears.
   Loaded by /masterclasses and /resources, which sell out of the same Payhip
   store and now render it identically. Tokens come from shared.css. */

/* Level filter. Twenty-seven cards in one grid opens with nine National 5
   classes, so a parent who came for Higher sees a screen of the wrong course
   and has no way to know they are in the right place. These chips deep-link
   (?level=higher), so arriving from the homepage lands on that level alone
   with the chip lit.

   The :not([hidden]) matters. A bare .lvlbar{display:flex} is an author
   declaration and beats the user agent's [hidden]{display:none}, so the bar
   was never actually hidden: when there was only one level to show, or when
   the feed failed, an empty role="group" called "Filter by level" stayed in
   the accessibility tree with nothing inside it. */
.lvlbar:not([hidden]){display:flex;flex-wrap:wrap;gap:9px;justify-content:center;margin:0 0 30px}
.lvlbar button{border-radius:999px;padding:10px 20px;font-family:inherit;font-weight:600;font-size:14.2px;
  border:1.6px solid var(--line);background:#fff;color:var(--ink);cursor:pointer;transition:.25s}
.lvlbar button:hover{border-color:var(--teal);color:var(--teal)}
.lvlbar button[aria-pressed="true"]{background:var(--grad);color:#fff;border-color:transparent;
  box-shadow:0 12px 26px -14px rgba(59,130,246,.8)}
.lvlbar button:focus-visible{outline:2px solid var(--teal);outline-offset:3px}

/* Three columns, one per level, side by side. All three levels are then on
   screen at once, so nobody has to work out whether they are looking at their
   own course — which no amount of sorting a single list achieves.

   The track count is --cols, set from the number of columns actually built.
   It used to be a hardcoded repeat(3,1fr) while the column count came from
   her data, so the fourth column that the code deliberately creates for an
   unrecognised product had no track to sit in: it wrapped under National 5
   with no row gap and, being a sibling, took the divider with it — a stray
   vertical rule hanging outside the page's left edge. */
.shopcols{display:grid;grid-template-columns:1fr;gap:34px;align-items:start}
@media(min-width:900px){
  .shopcols{grid-template-columns:repeat(var(--cols,3),minmax(0,1fr))}
}
.lvlcol{display:flex;flex-direction:column;gap:18px;padding:0 0 10px}
@media(min-width:900px){
  /* A rule between columns, as on her sketch. Drawn on the column rather than
     between rows so it runs the full height of the tallest one.

     The pull and the padding must match. With padding-left:34px against a
     -17px margin, columns two and three lost 18px of content that column one
     kept — National 5's cards rendered 357px wide against 339px for the other
     two at 1280px — and each rule sat 17px from the cards on its left and 34px
     from those on its right. On a layout whose whole argument is that the
     three levels are equal peers, one was visibly bigger and both rules were
     visibly off-centre. */
  .lvlcol + .lvlcol{border-left:1px solid var(--line);padding-left:17px;margin-left:-17px}
}

/* Column heading. Centred over its column, set large in her gradient — this
   is the thing a parent scans for, so it should read as a title and not as a
   caption. Sticky, so the level stays named while they scroll nine dates. */
.lvlhead{text-align:center;padding:16px 0 15px;margin-bottom:6px;
  border-bottom:2px solid var(--line);
  /* Tinted to match the section it sits in, not white — on this panel a white
     band reads as another card. Blurred like the nav so the molecule
     background still moves under it while a column scrolls past. */
  position:sticky;top:60px;z-index:2;
  background:rgba(243,250,250,.93);backdrop-filter:blur(8px)}
.lvlhead h3{font-size:clamp(1.7rem,3.4vw,2.2rem);font-weight:700;line-height:1.1;
  background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent}
.lvlcount{display:block;margin-top:5px;font-size:12.4px;font-weight:600;
  letter-spacing:1.4px;text-transform:uppercase;color:var(--mut)}

.buyrow{display:grid;gap:8px}
.buyrow .btn{width:100%;justify-content:center}

/* The buy and basket controls are real buttons, not anchors wearing
   role="button": as <a href="#"> they promised Space activation that anchors
   do not give, and an unbound one only ever threw the reader to the top of
   the page.

   The colour modifiers have to be restated here. .btn-p and .btn-g are plain
   class selectors in shared.css, so the element+class reset below outranks
   them — which left "Add to basket" painting as a blank white pill with the
   gradient stripped off it. */
button.btn{font:inherit;font-weight:600;font-size:14.6px;cursor:pointer;border:0;background:none}
button.btn-p{background:var(--grad);color:#fff}
button.btn-g{border:1.6px solid var(--line);background:#fff;color:var(--ink)}
button.btn-g:hover{border-color:var(--teal);color:var(--teal)}
button.btn:focus-visible{outline:2px solid var(--teal);outline-offset:3px}

/* The pass leads its column: it is the offer she most wants taken, and the
   one a parent is actually deciding about. An ember travels its outline —
   one per level, in that level's own colour, taken from the three stops of
   her brand gradient rather than invented:
     National 5 teal · Higher blue · Advanced Higher violet.
   Only the passes carry it. If every card glowed it would be wallpaper and
   would single out nothing. */
@property --ember-a{syntax:'<angle>';initial-value:0deg;inherits:false}

.lvlcol{--ember:#12A594}                    /* so the ring never renders unstyled */
.lvlcol[data-level="n5"]{--ember:#12A594}
.lvlcol[data-level="hi"]{--ember:#3B82F6}
.lvlcol[data-level="ah"]{--ember:#8B5CF6}
.lvlcol[data-level="other"]{--ember:#F0A500}

/* The border is tinted, not transparent, and the ring paints in the 1.5px
   band outside it. Two separate things can stop the ring rendering — no
   @property (the angle cannot interpolate, so the whole background is
   invalid) and no color-mix() (the declaration is dropped at parse time) —
   and on a transparent border either one left the three pass cards as the
   only cards on the page with no outline at all: bare white rectangles,
   on exactly the product she most wants bought.

   There used to be an @supports guard for this. It could never match:
   a declaration containing var() is treated as valid at parse time in every
   browser, including ones with no @property at all, so `@supports not
   (background:conic-gradient(from var(--ember-a),red,blue))` was false
   everywhere and the fallback inside it never ran. Making the border the
   base state needs no feature test and covers both causes. */
.card.is-pass{position:relative;border-color:var(--ember);
  box-shadow:0 16px 34px -20px color-mix(in srgb,var(--ember) 70%,transparent)}
.card.is-pass::after{
  content:"";position:absolute;inset:-1.5px;border-radius:inherit;padding:1.5px;
  pointer-events:none;
  /* A dim ring for the whole outline, with one bright ember riding round it. */
  background:
    conic-gradient(from var(--ember-a,0deg),
      color-mix(in srgb,var(--ember) 22%,transparent) 0 68%,
      color-mix(in srgb,var(--ember) 60%,transparent) 79%,
      var(--ember) 86%,
      #fff 89%,
      var(--ember) 92%,
      color-mix(in srgb,var(--ember) 30%,transparent) 97%,
      color-mix(in srgb,var(--ember) 22%,transparent) 100%);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
          mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
          mask-composite:exclude;
  animation:ember 5.5s linear infinite;
}
/* Staggered so the passes are not in lockstep, which reads as a single
   mechanism rather than three cards. */
.lvlcol[data-level="hi"] .card.is-pass::after{animation-delay:-1.8s}
.lvlcol[data-level="ah"] .card.is-pass::after{animation-delay:-3.6s}
@keyframes ember{to{--ember-a:360deg}}

/* shared.css kills animation with *{animation:none!important}, and the
   universal selector matches elements, not pseudo-elements. This rule is
   what actually stops the ember, and is not redundant. */
@media (prefers-reduced-motion:reduce){
  .card.is-pass::after{animation:none}
  .lvlbar button{transition:none}
}

/* Filtering hides whole columns — one attribute, so a heading and its cards
   can never disagree about whether they are showing. On a phone, where the
   columns stack and National 5 would sit on top of everyone again, this is
   what keeps a Higher parent looking at Higher. */
.shopcols:not([data-filter="all"]){grid-template-columns:1fr}
.shopcols:not([data-filter="all"]) .lvlcol{
  border-left:0;padding-left:0;margin-left:0;
  /* One level on its own gets the full width back, rather than staying a
     narrow strip where two columns used to be. */
  display:grid;grid-template-columns:repeat(auto-fit,minmax(258px,1fr));gap:18px}
.shopcols:not([data-filter="all"]) .lvlhead{grid-column:1/-1}

/* Hiding comes last and names .lvlcol deliberately. The rule above sets
   display:grid on the same elements at equal specificity, so written the
   other way round the filter silently stopped working — every column stayed
   on screen and only the chip looked selected. */
.shopcols[data-filter="n5"] > .lvlcol:not([data-level="n5"]),
.shopcols[data-filter="hi"] > .lvlcol:not([data-level="hi"]),
.shopcols[data-filter="ah"] > .lvlcol:not([data-level="ah"]),
.shopcols[data-filter="other"] > .lvlcol:not([data-level="other"]){display:none}

/* While the feed is in flight. The section used to carry `hidden` until the
   products arrived, which made every button pointing at it a dead click for
   as long as the fetch took — including the hero's own "Browse the Shop".
   The section is always there now; only its contents change. */
.shopwait{grid-column:1/-1;text-align:center}
.shopwait p{margin-bottom:14px}

/* Announcements for anything that has no visible confirmation of its own —
   adding to the basket opens a cross-origin Payhip drawer at the end of
   <body>, which is announced to nobody. */
.shop-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

/* ---------- the basket -------------------------------------------------
   Hers, on her own domain. Payhip's cart was a cross-origin iframe that
   Safari and the in-app browsers refuse to give storage to, so the whole
   thing lives here now and Payhip only sees it at the checkout step.

   Every [hidden] rule below is written as its own attribute selector rather
   than trusting the user agent's [hidden]{display:none}: an author display
   rule beats the UA sheet, which is exactly how the level filter bar ended
   up "hidden" and still on screen. */

/* the bar along the bottom, once there is something to check out */
.bk-bar{position:fixed;left:0;right:0;bottom:0;z-index:95;
  display:flex;align-items:center;justify-content:center;gap:16px;flex-wrap:wrap;
  padding:12px 16px calc(12px + env(safe-area-inset-bottom));
  background:rgba(255,255,255,.94);backdrop-filter:blur(12px);
  border-top:1px solid var(--line);box-shadow:0 -10px 30px -22px rgba(14,59,71,.5)}
.bk-bar[hidden]{display:none}
.bk-sum{font-weight:600;color:var(--ink);font-size:.95rem}
/* the WhatsApp button sits bottom-right and would land underneath it */
body.has-basket .wa-float{bottom:84px}
/* And the page has to be able to scroll clear of the bar. Without this the
   bar sits over the Add to basket button of whichever card is at the bottom
   of the screen, and the tap lands on the bar instead of the card. */
body.has-basket{padding-bottom:76px}

.bk-scrim{position:fixed;inset:0;z-index:96;background:rgba(6,25,32,.44);
  backdrop-filter:blur(2px)}
.bk-scrim[hidden]{display:none}
body.bk-open{overflow:hidden}

.bk-panel{position:fixed;z-index:97;right:0;top:0;bottom:0;width:min(430px,100%);
  display:flex;flex-direction:column;background:var(--paper);
  border-left:1px solid var(--line);box-shadow:-24px 0 60px -30px rgba(14,59,71,.5)}
.bk-panel[hidden]{display:none}
@media(max-width:520px){
  /* A sheet from the bottom on a phone, where a full-height side panel reads
     as the page having been replaced. */
  .bk-panel{top:auto;left:0;width:100%;max-height:86vh;border-left:0;
    border-top-left-radius:22px;border-top-right-radius:22px;
    border-top:1px solid var(--line)}
}
.bk-head{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:20px 20px 14px;border-bottom:1px solid var(--line)}
.bk-head h3{font-size:1.3rem}
.bk-x{width:38px;height:38px;flex:0 0 auto;border:1px solid var(--line);background:#fff;
  border-radius:50%;font-size:1.3rem;line-height:1;color:var(--ink);cursor:pointer;
  font-family:inherit;transition:.25s}
.bk-x:hover{border-color:var(--teal);color:var(--teal)}

.bk-items{flex:1;overflow-y:auto;padding:8px 20px;-webkit-overflow-scrolling:touch}
.bk-empty{text-align:center;color:var(--mut);padding:36px 0}
.bk-row{display:grid;grid-template-columns:56px 1fr auto auto;gap:12px;align-items:center;
  padding:14px 0;border-bottom:1px solid var(--line)}
.bk-row:last-child{border-bottom:0}
.bk-row img{width:56px;height:40px;object-fit:cover;border-radius:8px}
/* min-width:0 or a long unbroken product name — "Formula/balancedequations"
   — holds the column open at its min-content width and shoves the quantity
   and remove controls off the edge of the panel. */
.bk-meta{min-width:0}
.bk-meta b{display:block;font-family:var(--ser);font-size:.97rem;color:var(--ink);
  line-height:1.25;font-weight:600;overflow-wrap:anywhere}
.bk-meta span{font-size:.84rem;color:var(--mut)}
.bk-qty{display:flex;align-items:center;gap:2px;border:1px solid var(--line);
  border-radius:999px;padding:2px;flex:0 0 auto}
.bk-qty button{width:28px;height:28px;border:0;background:none;border-radius:50%;
  font-size:1rem;line-height:1;color:var(--ink);cursor:pointer;font-family:inherit}
.bk-qty button:hover{background:var(--tint2);color:var(--teal)}
.bk-qty span{min-width:20px;text-align:center;font-weight:600;font-size:.9rem}
.bk-rm{width:30px;height:30px;border:0;background:none;color:var(--mut);
  font-size:1.15rem;line-height:1;cursor:pointer;border-radius:50%;font-family:inherit}
.bk-rm:hover{background:#FDECEC;color:#C0392B}

.bk-foot{padding:16px 20px calc(18px + env(safe-area-inset-bottom));
  border-top:1px solid var(--line);background:var(--tint)}
.bk-total{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:14px}
.bk-total[hidden]{display:none}
.bk-total span{color:var(--mut);font-size:.92rem}
.bk-total b{font-family:var(--ser);font-size:1.5rem;color:var(--ink)}
.bk-foot .btn{width:100%;justify-content:center}
.bk-foot .btn + .btn{margin-top:9px}
.bk-go[hidden]{display:none}
.bk-note{margin-top:12px;font-size:.8rem;color:var(--mut);line-height:1.5;text-align:center}
.bk-clear{display:block;margin:10px auto 0;border:0;background:none;font-family:inherit;
  font-size:.82rem;color:var(--mut);text-decoration:underline;cursor:pointer}
.bk-clear:hover{color:#C0392B}

/* "Added ✓", briefly, so a tap that deliberately does not open the basket
   still visibly did something. */
.bk-add.is-added{background:#0B7F72;box-shadow:none}

.bk-x:focus-visible,.bk-qty button:focus-visible,.bk-rm:focus-visible,
.bk-clear:focus-visible,.bk-add:focus-visible{outline:2px solid var(--teal);outline-offset:2px}

@media (prefers-reduced-motion:reduce){
  .bk-x,.bk-add{transition:none}
}
