/* Theme base styles */
/* bundle: 2026-08-31a — see sop/build/B-02, Trap 0b. Editing an INCLUDED partial
   does not reliably regenerate template_main.min.css; the bundle keeps serving
   old CSS under an unchanged version string. Touching this file does. */

/* Tools
Any animations, or functions used throughout the project.
Note: _macros.css needs to be imported into each stylesheet where macros are used and not included here
*/

/* Generic
This is where reset, normalize & box-sizing styles go.
*/

*, *:before, *:after {
  box-sizing: border-box;
}
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */

/* Document
   ========================================================================== */

/**
 * 1. Correct the line height in all browsers.
 * 2. Prevent adjustments of font size after orientation changes in iOS.
 */

html {
  line-height: 1.15; /* 1 */
  -webkit-text-size-adjust: 100%; /* 2 */
}

/* Sections
   ========================================================================== */

/**
 * Remove the margin in all browsers.
 */

body {
  margin: 0;
}

/**
 * Correct the font size and margin on `h1` elements within `section` and
 * `article` contexts in Chrome, Firefox, and Safari.
 */

h1 {
  font-size: 2em;
  margin: 0.67em 0;
}

/* Grouping content
   ========================================================================== */

/**
 * Add the correct box sizing in Firefox.
 */

hr {
  box-sizing: content-box;
  height: 0;
}

/**
 * 1. Correct the inheritance and scaling of font size in all browsers.
 * 2. Correct the odd `em` font sizing in all browsers.
 */

pre {
  font-family: monospace, monospace; /* 1 */
  font-size: 1em; /* 2 */
}

/* Text-level semantics
   ========================================================================== */

/**
 * 1. Remove the bottom border in Chrome 57-
 * 2. Add the correct text decoration in Chrome, Edge, Opera, and Safari.
 */

abbr[title] {
  border-bottom: none; /* 1 */
  text-decoration: underline; /* 2 */
  text-decoration: underline dotted; /* 2 */
}

/**
 * Add the correct font weight in Chrome, Edge, and Safari.
 */

b,
strong {
  font-weight: bolder;
}

/**
 * 1. Correct the inheritance and scaling of font size in all browsers.
 * 2. Correct the odd `em` font sizing in all browsers.
 */

code,
kbd,
samp {
  font-family: monospace, monospace; /* 1 */
  font-size: 1em; /* 2 */
}

/**
 * Add the correct font size in all browsers.
 */

small {
  font-size: 80%;
}

/**
 * Prevent `sub` and `sup` elements from affecting the line height in
 * all browsers.
 */

sub,
sup {
  font-size: 75%;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}

sub {
  bottom: -0.25em;
}

sup {
  top: -0.5em;
}

/* Forms
   ========================================================================== */

/**
 * 1. Change the font styles in all browsers.
 * 2. Remove the margin in Firefox and Safari.
 */

button,
input,
optgroup,
select,
textarea {
  font-family: inherit; /* 1 */
  font-size: 100%; /* 1 */
  line-height: 1.15; /* 1 */
  margin: 0; /* 2 */
}

/**
 * Remove the inheritance of text transform in Edge and Firefox.
 * 1. Remove the inheritance of text transform in Firefox.
 */

button,
select { /* 1 */
  text-transform: none;
}

/**
 * Correct the inability to style clickable types in iOS and Safari.
 */

button,
[type="button"],
[type="reset"],
[type="submit"] {
  -webkit-appearance: button;
}

/**
 * Remove the inner border and padding in Firefox.
 */

button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
  border-style: none;
  padding: 0;
}

/**
 * Restore the focus styles unset by the previous rule.
 */

button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
  outline: 1px dotted ButtonText;
}

/**
 * Correct the padding in Firefox.
 */

fieldset {
  padding: 0.35em 0.75em 0.625em;
}

/**
 * Remove the padding so developers are not caught out when they zero out `fieldset` elements in all browsers.
 */

legend {
  padding: 0;
}

/**
 * Add the correct vertical alignment in Chrome, Firefox, and Opera.
 */

progress {
  vertical-align: baseline;
}

/**
 * Correct the cursor style of increment and decrement buttons in Chrome.
 */

[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
  height: auto;
}

/**
 * 1. Correct the odd appearance in Chrome and Safari.
 * 2. Correct the outline style in Safari.
 */

[type="search"] {
  -webkit-appearance: textfield; /* 1 */
  outline-offset: -2px; /* 2 */
}

/**
 * Remove the inner padding in Chrome and Safari on macOS.
 */

[type="search"]::-webkit-search-decoration {
  -webkit-appearance: none;
}

/**
 * 1. Correct the inability to style clickable types in iOS and Safari.
 * 2. Change font properties to `inherit` in Safari.
 */

::-webkit-file-upload-button {
  -webkit-appearance: button; /* 1 */
  font: inherit; /* 2 */
}

/* Interactive
   ========================================================================== */

/*
 * Add the correct display in Edge and Firefox.
 */

details {
  display: block;
}

/*
 * Add the correct display in all browsers.
 */

summary {
  display: list-item;
}

/* Objects
Non-cosmetic design patterns including grid and layout classes)
*/



/* CSS variables */

:root {
  --column-gap: 2.13%;
  --column-width-multiplier: 8.333;
}

/* Mobile layout */

.row-fluid {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
}


  .row-fluid .span1,
  .row-fluid .span2,
  .row-fluid .span3,
  .row-fluid .span4,
  .row-fluid .span5,
  .row-fluid .span6,
  .row-fluid .span7,
  .row-fluid .span8,
  .row-fluid .span9,
  .row-fluid .span10,
  .row-fluid .span11,
  .row-fluid .span12{
  min-height: 1px;
  width: 100%;
}

/* Desktop layout */

@media (min-width: 768px) {
  .row-fluid {
    flex-wrap: nowrap;
    justify-content: space-between;
  }

  
    .row-fluid .span1 {
      width: calc(var(--column-width-multiplier) * 1% * 1 - var(--column-gap) * (11 * var(--column-width-multiplier) / 100));
    }
  
    .row-fluid .span2 {
      width: calc(var(--column-width-multiplier) * 1% * 2 - var(--column-gap) * (10 * var(--column-width-multiplier) / 100));
    }
  
    .row-fluid .span3 {
      width: calc(var(--column-width-multiplier) * 1% * 3 - var(--column-gap) * (9 * var(--column-width-multiplier) / 100));
    }
  
    .row-fluid .span4 {
      width: calc(var(--column-width-multiplier) * 1% * 4 - var(--column-gap) * (8 * var(--column-width-multiplier) / 100));
    }
  
    .row-fluid .span5 {
      width: calc(var(--column-width-multiplier) * 1% * 5 - var(--column-gap) * (7 * var(--column-width-multiplier) / 100));
    }
  
    .row-fluid .span6 {
      width: calc(var(--column-width-multiplier) * 1% * 6 - var(--column-gap) * (6 * var(--column-width-multiplier) / 100));
    }
  
    .row-fluid .span7 {
      width: calc(var(--column-width-multiplier) * 1% * 7 - var(--column-gap) * (5 * var(--column-width-multiplier) / 100));
    }
  
    .row-fluid .span8 {
      width: calc(var(--column-width-multiplier) * 1% * 8 - var(--column-gap) * (4 * var(--column-width-multiplier) / 100));
    }
  
    .row-fluid .span9 {
      width: calc(var(--column-width-multiplier) * 1% * 9 - var(--column-gap) * (3 * var(--column-width-multiplier) / 100));
    }
  
    .row-fluid .span10 {
      width: calc(var(--column-width-multiplier) * 1% * 10 - var(--column-gap) * (2 * var(--column-width-multiplier) / 100));
    }
  
    .row-fluid .span11 {
      width: calc(var(--column-width-multiplier) * 1% * 11 - var(--column-gap) * (1 * var(--column-width-multiplier) / 100));
    }
  
}
/* max-width previously came from theme-overrides.css, which no longer loads on
   Glow pages (the Box Rule, B-06). System pages need the cap. */
.content-wrapper {
  max-width: 1240px;
  margin: 0 auto;
  padding: 0 1rem;
}

@media screen and (min-width: 1380px) {
  .content-wrapper {
    padding: 0;
  }
}

.dnd-section > .row-fluid {
  margin: 0 auto;
}

.dnd-section .dnd-column {
  padding: 0 1rem;
}

@media (max-width: 767px) {
  .dnd-section .dnd-column {
    padding: 0;
  }
}

/* Elements
Base HTML elements are styled in this section (<body>, <h1>, <a>, <p>, <button> etc.)
*/

/* The overflow-wrap is meant to prevent long/large words from breaking the mobile responsiveness of a page (e.g. horizontal scrolling). It is preferred to reduce font sizes on mobile to address this, with this CSS specifically helping with extreme scenarios where a reduction in font size is not possible. */

body {
  line-height: 1.4;
  overflow-wrap: break-word;
}

/* Handles word breaking for a few specific languages which handle breaks in words differently. If your content is not translated into these languages, you can safely remove this.  */

html[lang^="ja"] body,
html[lang^="zh"] body,
html[lang^="ko"] body {
  line-break: strict;
  overflow-wrap: normal;
  word-break: break-all;
}

/* Paragraphs */

p {
  font-size: 1rem;
  margin: 0 0 1.4rem;
}

/* Anchors */

a {
  cursor: pointer;
}

/* Headings */

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0 0 1.4rem;
}

/* Lists */

ul,
ol {
  margin: 0 0 1.4rem;
}

ul ul,
ol ul,
ul ol,
ol ol {
  margin: 0;
}

ul.no-list {
  list-style: none;
  margin: 0;
  padding-left: 0;
}

/* Code blocks */

pre {
  overflow: auto;
}

code {
  vertical-align: bottom;
}

/* Blockquotes */

blockquote {
  border-left: 2px solid;
  margin: 0 0 1.4rem;
  padding-left: 0.7rem;
}

/* Horizontal rules */

hr {
  border: none;
  border-bottom: 1px solid #CCC;
}

/* Image alt text */

img {
  font-size: 0.583rem;
  word-break: normal;
}
button,
.button,
.hs-button {
  cursor: pointer;
  display: inline-block;
  text-align: center;
  transition: all 0.15s linear;
  white-space: normal;
}

/* Scoped 30 Aug 2026 (B-06, the Box Rule). This was unscoped and grey-slabbed
   every disabled Glow control — the cart quantity stepper and the intake sign
   button both rendered #D0D0D0. Glow controls define their own :disabled. */
.hs-form button:disabled,
.systems-page button:disabled,
.hs-button:disabled {
  background-color: #D0D0D0;
  border-color: #D0D0D0;
  color: #E6E6E6;
}

/* No button */

.no-button,
.no-button:hover,
.no-button:focus,
.no-button:active {
  background: none;
  border: none;
  border-radius: 0;
  color: initial;
  font-family: inherit;
  font-size: inherit;
  font-style: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
  line-height: inherit;
  margin-bottom: 0;
  padding: 0;
  text-align: left;
  text-decoration: none;
  transition: none;
}
/* Fields */

.hs-form-field {
  margin-bottom: 1.4rem;
}

/* Labels */

/* Scoped 30 Aug 2026 (B-06). Unscoped `form ...` rules used attribute selectors
   at (0,1,2) and beat Glow's own field classes at (0,1,0) — /contact and
   /checkout were shipping HubSpot's form chrome. These now apply only to
   HubSpot-generated forms, which is what they were written for. */
/* ⚠ NOT #hs-membership-form. An ID selector (1,0,1) outranks
   `.glow-auth__form input[type=checkbox] + label` (0,2,1) in _portal.css, which
   is what keeps the "Stay signed in" checkbox inline with its label — scoping
   here with an ID stacked them. The membership screens are fully styled by
   _portal.css's .glow-auth rules and need nothing from this file. */
.hs-form label,
.systems-page form label {
  display: block;
  font-size: 0.875rem;
  margin-bottom: 0.35rem;
}

/* Form Title */
.form-title {
  margin-bottom: 0;
}

/* Help text */

form legend {
  font-size: 0.875rem;
}

/* Inputs */

.hs-form input[type=text],
.hs-form input[type=search],
.hs-form input[type=email],
.hs-form input[type=password],
.hs-form input[type=tel],
.hs-form input[type=number],
.hs-form input[type=file],
.hs-form select,
.hs-form textarea,
.systems-page form input[type=text],
.systems-page form input[type=search],
.systems-page form input[type=email],
.systems-page form input[type=password],
.systems-page form input[type=tel],
.systems-page form input[type=number],
.systems-page form select,
.systems-page form textarea {
  display: inline-block;
  font-size: 0.875rem;
  padding: 0.7rem;
  width: 100%;
}

form textarea {
  resize: vertical;
}

form fieldset {
  max-width: 100% !important;
}

/* Inputs - checkbox/radio */

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

form .inputs-list > li {
  display: block;
  margin: 0.7rem 0;
}

form .inputs-list input,
form .inputs-list span {
  vertical-align: middle;
}

form input[type=checkbox],
form input[type=radio] {
  cursor: pointer;
  margin-right: 0.35rem;
}

/* Inputs - date picker */

.hs-dateinput {
  position: relative;
}

.hs-dateinput:before {
  content:'\01F4C5';
  position: absolute;
  right: 10%;
  top: 50%;
  transform: translateY(-50%);
}

.fn-date-picker .pika-table thead th {
  color: #FFF;
}

.fn-date-picker td.is-selected .pika-button {
  border-radius: 0;
  box-shadow: none;
}

.fn-date-picker td .pika-button:hover,
.fn-date-picker td .pika-button:focus {
  border-radius: 0 !important;
  color: #FFF;
}

/* Inputs - file picker */

form input[type=file] {
  background-color: transparent;
  border: initial;
  padding: initial;
}

/* Headings and text */

form .hs-richtext,
form .hs-richtext p {
  font-size: 0.875rem;
  margin: 0 0 1.4rem;
}

form .hs-richtext img {
  max-width: 100% !important;
}

/* GDPR */

.legal-consent-container .hs-form-booleancheckbox-display > span,
.legal-consent-container .hs-form-booleancheckbox-display > span p {
  margin-left: 1rem !important;
}

/* Validation */

.hs-form-required {
  color: #EF6B51;
}

.hs-input.invalid.error {
  border-color: #EF6B51;
}

.hs-error-msg {
  color: #EF6B51;
  margin-top: 0.35rem;
}

/* Submit button */

form input[type=submit],
form .hs-button {
  cursor: pointer;
  display: inline-block;
  text-align: center;
  transition: all 0.15s linear;
  white-space: normal;
}

/* Captcha */

.grecaptcha-badge {
  margin: 0 auto;
}


  /* Search button input field and suggestions */
  .body-container-wrapper .hs-search-field__button {
    padding: 15px;
  }

  .body-container-wrapper .hs-search-field__bar--button-inline .hs-search-field__button {
    margin-left: 6px;
    margin-bottom: 0;
  }

  .body-container-wrapper .hs-search-field__button svg {
    height: 15px;
    fill: #fff;
  }

  .body-container-wrapper .hs-search-field__bar > form > .hs-search-field__input {
    padding: 10px;
  }

  .body-container-wrapper .hs-search-field__suggestions li a {
    color: #494A52;
    padding: 0.35rem 0.7rem;
    text-decoration: none;
    transition: background-color 0.3s;
  }

/* Table */

table {
  border-collapse: collapse;
  margin-bottom: 1.4rem;
  overflow-wrap: break-word;
}

/* Table cells */

td,
th {
  vertical-align: top;
}

/* Table header */

thead th {
  vertical-align: bottom;
}

/* Components
Specific pieces of UI that are stylized. Typically used for global partial styling
*/

/* Header DND sections */

.header .dnd-section {
  padding: 0;
}

/* Header container */

.header__container {
  display: flex;
  justify-content: space-between;
}

.header__row-1 {
  padding-top: 1rem;
}

.header__row-1,
.header__row-2 {
  align-items: center;
  display: flex;
  justify-content: flex-end;
  width: 100%;
}

@media (max-width: 1150px) and (min-width: 767px) {
  .header__column {
    width: 100%;
  }
}

@media (max-width: 767px) {
  .header__container {
    flex-direction: column;
    padding: 1rem 0 0;
  }

  .header__column {
    position: relative;
  }

  .header__row-1 {
    padding-top: 0;
  }

  .header__row-2 {
    justify-content: center;
    padding: 1.05rem;
  }
}

/* Navigation skipper */

.header__skip {
  height: 1px;
  left: -1000px;
  overflow: hidden;
  position: absolute;
  text-align: left;
  top: -1000px;
  width: 1px;
}

.header__skip:hover,
.header__skip:focus,
.header__skip:active {
  height: auto;
  left: 0;
  overflow: visible;
  top: 0;
  width: auto;
}

/* Logo */

.header__logo {
  align-items: center;
  display: flex;
  height: auto;
  margin-right: auto;
  max-width: 200px;
  overflow: hidden;
}

@media (max-width: 767px) {
  .header__logo {
    margin: 0 auto;
    width: 100%;
  }
}

.header__logo img {
  max-width: 100%;
}

.header__logo .logo-company-name {
  font-size: 1.167rem;
  margin-top: 0.7rem;
}

.header__logo--main {
  padding-top: 1rem;
}

/* Search bar */

.header__search {
  padding: 0 1rem;
  width: auto;
}



  .hs-search-field__form {
    position: relative;
  }

  .header__search .hs-search-field__label {
    flex-basis: auto;
  }



.header__search .hs-search-field__input {
  
  height: 45px;
  padding: 0 0.7rem;
}



  .header__search .hs-search-field__button {
    padding: 0;
    fill: #000;
    background-color: transparent;
    border: none;
    padding: 10px;
    position: absolute;
    top: 0;
    right: 0;
  }

  .header__search .hs-search-field__button svg {
    height: 25px;
  }


.header__search .hs-search-field--open .hs-search-field__input {
  border-bottom: none;
  border-radius: 6px 6px 0 0;
  max-width: 100%;
}

.header__search .hs-search-field--open .hs-search-field__suggestions {
  background-color: #FFF;
  border: 2px solid #D1D6DC;
  border-radius: 0 0 6px 6px;
  border-top-width: 1px;
  position: absolute;
  width: 100%;
  z-index: 10;
}

.header__search .hs-search-field__suggestions li {
  border-top: 1px solid #D1D6DC;
  font-size: 0.875rem;
}

.header__search .hs-search-field__suggestions li a {
  color: #494A52;
  padding: 0.35rem 0.7rem;
  text-decoration: none;
  transition: background-color 0.3s;
}

.header__search .hs-search-field__suggestions #results-for {
  display: none;
}

@media (min-width: 767px) {
  .header__search form {
    align-items: center;
    display: flex;
    flex-direction: row;
  }

  .header__search label {
    margin: 0 1rem 0 0;
  }

  .header__search .hs-search-field__input {
    width: auto;
  }
}

@media (max-width: 767px) {
  .header__search {
    border-top: 2px solid #CED4DB;
    order: 1;
    padding: 1.05rem;
  }
}

/* Language switcher */

.header__language-switcher {
  cursor: pointer;
  padding-right: 1.4rem;
}

.header__language-switcher .lang_switcher_class {
  position: static;
}

.header__language-switcher .lang_list_class {
  border: 2px solid;
  border-radius: 3px;
  box-shadow: 0 2px 9px 0 rgba(0, 0, 0, 0.2);
  display: block;
  left: calc(100% - 24px);
  opacity: 0;
  min-width: 100px;
  padding-top: 0;
  text-align: left;
  top: 100%;
  transition: opacity 0.3s;
  visibility: hidden;
}

.header__language-switcher:hover .lang_list_class,
.header__language-switcher:focus .lang_list_class {
  opacity: 1;
  transition: opacity 0.3s;
  visibility: visible;
}

.header__language-switcher .lang_list_class:before {
  left: 70%;
  top: -25px;
}

.header__language-switcher .lang_list_class:after {
  left: 70%;
  top: -22px;
}

.header__language-switcher .lang_list_class.first-active::after {
  top: -22px;
  transition: 0.3s;
}

.header__language-switcher .lang_list_class li {
  border: none;
  font-size: 18px;
  padding: 0.35rem 0.7rem;
}

.header__language-switcher .lang_list_class li:first-child {
  border-radius: 6px 6px 0 0;
  border-top: none;
}

.header__language-switcher .lang_list_class li:last-child {
  border-bottom: none;
  border-radius: 0 0 6px 6px;
}

.header__language-switcher .lang_list_class li:hover {
  transition: background-color 0.3s;
}

.header__language-switcher--label {
  display: flex;
  position: relative;
}

.header__language-switcher--label-current {
  align-items: center;
  display: flex;
  font-size: 0.75rem;
  margin-bottom: 0.175rem;
  margin-left: 0.7rem;
}

.header__language-switcher--label-current:after {
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 6px solid #494A52;
  content: "";
  display: block;
  height: 0px;
  margin-left: 0.7rem;
  margin-top: 0.175rem;
  width: 0px;
}

@media (max-width: 767px) {
  .header__language-switcher {
    border-top: 2px solid #CED4DB;
    padding-left: 1.05rem;
    padding-right: 0;
  }

  .header__language-switcher .lang_list_class {
    border: none;
    box-shadow: unset;
    display: block;
    left: 30px;
    opacity: 1;
    padding: 0 1.05rem;
    top: 0;
    visibility: visible;
  }

  .header__language-switcher .lang_list_class li {
    background-color: inherit;
    font-size: 0.917rem;
  }

  .header__language-switcher--label-current {
    display: none;
  }

  .header__language-switcher .globe_class {
    background-image: none;
  }

  .header__language-switcher .lang_list_class li:hover{
    background-color: inherit;
  }

  .header__language-switcher .lang_list_class:before,
  .header__language-switcher .lang_list_class:after {
    content: none;
  }
    /* V1 lang switcher updates to keep "in line" w/ v0 mobile styles */
    .header__language-switcher .hs-language-switcher__menu {
        display: block;
        box-shadow:none!important;
        background: transparent;
    }
    .header__language-switcher .hs-language-switcher__menu a {
        font-size: 20px!important;
    }
    .header__language-switcher .hs-language-switcher__button {
        display: none;
    }
  }

/* Navigation */

#nav-toggle {
  display: none;
}

/* Mobile toggles */

@media (max-width: 767px) {
  .header__navigation,
  .header__search,
  .header__language-switcher {
    display: none;
    width: 100%;
  }

  .header__navigation.open,
  .header__search.open,
  .header__language-switcher.open {
    background-color: #F8FAFC;
    display: block;
    left: 0;
    min-height: calc(100vh - 115px);
    position: absolute;
    right: 0;
    top: 75px;
    z-index: 2;
  }

  .header__navigation--toggle,
  .header__search--toggle,
  .header__language-switcher--toggle,
  .header__close--toggle {
    cursor: pointer;
    margin: 0 5vw;
    position: relative;
  }

  .header__navigation--toggle.hide,
  .header__search--toggle.hide,
  .header__language-switcher--toggle.hide {
    display: none;
  }

  .header__navigation--toggle.open,
  .header__search--toggle.open,
  .header__language-switcher--toggle.open {
    display: block;
    margin-left: 0;
    margin-right: auto;
  }

  .header__navigation--toggle:after,
  .header__search--toggle:after,
  .header__language-switcher--toggle:after {
    display: none;
    font-size: 1.083rem;
    font-weight: 600;
    position: absolute;
    left: 40px;
    text-transform: uppercase;
    top: -10px;
  }

  .header__navigation--toggle.open:after,
  .header__search--toggle.open:after,
  .header__language-switcher--toggle.open:after {
    display: block;
    word-break: normal;
  }

  .header__navigation--toggle {
    background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAxOSIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4gICAgICAgIDx0aXRsZT5oYW1idXJnZXI8L3RpdGxlPiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4gICAgPGcgaWQ9ImhhbWJ1cmdlciIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+ICAgICAgICA8ZyBpZD0iR3JvdXAiIHN0cm9rZT0iIzQ5NEE1MiIgc3Ryb2tlLXdpZHRoPSIzIj4gICAgICAgICAgICA8cmVjdCBpZD0iUmVjdGFuZ2xlIiB4PSIxLjUiIHk9IjEuNSIgd2lkdGg9IjIxIiBoZWlnaHQ9IjEiIHJ4PSIwLjUiPjwvcmVjdD4gICAgICAgICAgICA8cmVjdCBpZD0iUmVjdGFuZ2xlLUNvcHktNCIgeD0iMS41IiB5PSI5LjUiIHdpZHRoPSIyMSIgaGVpZ2h0PSIxIiByeD0iMC41Ij48L3JlY3Q+ICAgICAgICAgICAgPHJlY3QgaWQ9IlJlY3RhbmdsZS1Db3B5LTUiIHg9IjEuNSIgeT0iMTcuNSIgd2lkdGg9IjIxIiBoZWlnaHQ9IjEiIHJ4PSIwLjUiPjwvcmVjdD4gICAgICAgIDwvZz4gICAgPC9nPjwvc3ZnPg==);
    background-size: cover;
    height: 25px;
    width: 25px;
  }

  .header__navigation--toggle:after {
    content: "Menu";
  }

  .header__language-switcher--toggle {
    background-image: url(//static.hsappstatic.net/cos-LanguageSwitcher/static-1.1/img/globe.png);
    background-size: cover;
    height: 25px;
    width: 25px;
  }

  .header__language-switcher--toggle:after {
    content: "Language";
  }

  .header__search--toggle {
    background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4gICAgICAgIDx0aXRsZT5TZWFyY2g8L3RpdGxlPiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4gICAgPGRlZnM+ICAgICAgICA8cGF0aCBkPSJNOS4xMzg2MTUzNCwxNS44OTI1Njg1IEM1LjQxMzk1NzQyLDE1Ljg5MjU2ODUgMi4zODM4ODUyNywxMi44NjM0NDc1IDIuMzgzODg1MjcsOS4xMzkwMDM3NiBDMi4zODM4ODUyNyw1LjQxNDU2MDA1IDUuNDEzOTU3NDIsMi4zODM4ODUyNyA5LjEzODYxNTM0LDIuMzgzODg1MjcgQzEyLjg2MzI3MzMsMi4zODM4ODUyNyAxNS44OTI1Njg1LDUuNDE0NTYwMDUgMTUuODkyNTY4NSw5LjEzOTAwMzc2IEMxNS44OTI1Njg1LDEyLjg2MzQ0NzUgMTIuODYzMjczMywxNS44OTI1Njg1IDkuMTM4NjE1MzQsMTUuODkyNTY4NSBNOS4xMzg3NTI0NSwyLjQzMzYwODg3ZS0xMyBDMTQuMTc3OTk1NSwyLjQzMzYwODg3ZS0xMyAxOC4yNzY0NTM3LDQuMTAwMzI0NzEgMTguMjc2NDUzNyw5LjEzOTI3Nzk2IEMxOC4yNzY0NTM3LDExLjIyOTgyMTEgMTcuNTcxMDE2OSwxMy4xNTg0NDM0IDE2LjM4NTYzMTMsMTQuNjk5NjY5NiBMMjMuNjUwODg4MSwyMS45NjUyMjY2IEMyNC4xMTYzNzA2LDIyLjQzMDcwOTIgMjQuMTE2MzcwNiwyMy4xODU0MDU1IDIzLjY1MDg4ODEsMjMuNjUwODg4MSBDMjMuMTg1NDA1NSwyNC4xMTYzNzA2IDIyLjQzMDcwOTIsMjQuMTE2MzcwNiAyMS45NjUyMjY2LDIzLjY1MDg4ODEgTDE0LjY5OTgxMzMsMTYuMzg1NDcxMyBDMTMuMTU4NDQwNSwxNy41NzA5NTA5IDExLjIyOTU3MzgsMTguMjc2NDUzNyA5LjEzODc1MjQ1LDE4LjI3NjQ1MzcgQzQuMDk5NTA5MzgsMTguMjc2NDUzNyAtMy43MzAzNDkzNmUtMTQsMTQuMTc4MjMxMiAtMy43MzAzNDkzNmUtMTQsOS4xMzkyNzc5NiBDLTMuNzMwMzQ5MzZlLTE0LDQuMTAwMzI0NzEgNC4wOTk1MDkzOCwyLjQzMzYwODg3ZS0xMyA5LjEzODc1MjQ1LDIuNDMzNjA4ODdlLTEzIFoiIGlkPSJwYXRoLTEiPjwvcGF0aD4gICAgPC9kZWZzPiAgICA8ZyBpZD0iU2VhcmNoIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4gICAgICAgIDxtYXNrIGlkPSJtYXNrLTIiIGZpbGw9IndoaXRlIj4gICAgICAgICAgICA8dXNlIHhsaW5rOmhyZWY9IiNwYXRoLTEiPjwvdXNlPiAgICAgICAgPC9tYXNrPiAgICAgICAgPHVzZSBpZD0iSWNvbnMvQWN0aW9ucy9TZWFyY2giIGZpbGw9IiM0OTRBNTIiIHhsaW5rOmhyZWY9IiNwYXRoLTEiPjwvdXNlPiAgICA8L2c+PC9zdmc+);
    background-size: cover;
    height: 25px;
    width: 25px;
  }

  .header__search--toggle:after {
    content: "Search";
  }

  .header__close--toggle {
    background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjE5cHgiIHZpZXdCb3g9IjAgMCAyNCAxOSIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4gICAgICAgIDx0aXRsZT5jbG9zZTwvdGl0bGU+ICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPiAgICA8ZyBpZD0iY2xvc2UiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPiAgICAgICAgPGcgaWQ9Ikdyb3VwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyLjAwMDAwMCwgLTEuMDAwMDAwKSIgc3Ryb2tlPSIjNDk0QTUyIiBzdHJva2Utd2lkdGg9IjMiPiAgICAgICAgICAgIDxyZWN0IGlkPSJSZWN0YW5nbGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwLjAwMDAwMCwgMTAuNTAwMDAwKSByb3RhdGUoLTQ1LjAwMDAwMCkgdHJhbnNsYXRlKC0xMC4wMDAwMDAsIC0xMC41MDAwMDApICIgeD0iLTAuNSIgeT0iMTAuNSIgd2lkdGg9IjIxIiBoZWlnaHQ9IjEiIHJ4PSIwLjUiPjwvcmVjdD4gICAgICAgICAgICA8cmVjdCBpZD0iUmVjdGFuZ2xlLUNvcHktNSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAuMDAwMDAwLCAxMC41MDAwMDApIHJvdGF0ZSg0NS4wMDAwMDApIHRyYW5zbGF0ZSgtMTAuMDAwMDAwLCAtMTAuNTAwMDAwKSAiIHg9Ii0wLjUiIHk9IjEwLjUiIHdpZHRoPSIyMSIgaGVpZ2h0PSIxIiByeD0iMC41Ij48L3JlY3Q+ICAgICAgICA8L2c+ICAgIDwvZz48L3N2Zz4=);
    background-repeat: no-repeat;
    background-size: 110%;
    display: none;
    height: 25px;
    margin-right: 0;
    width: 25px;
  }

  .header__close--toggle.show {
    display: block;
  }
}
/* Menu and simple menu */

.hs-menu-wrapper ul {
  display: flex;
  flex-wrap: wrap;
  list-style: none;
  margin: 0;
  padding-left: 0;
}

/* Horizontal menu */

.hs-menu-wrapper.hs-menu-flow-horizontal .hs-menu-children-wrapper {
  flex-direction: column;
}

@media (max-width: 767px) {
  .hs-menu-wrapper.hs-menu-flow-horizontal ul {
    flex-direction: column;
  }
}

/* Vertical menu */

.hs-menu-wrapper.hs-menu-flow-vertical ul {
  flex-direction: column;
}

/* Flyouts */

.hs-menu-wrapper.hs-menu-flow-vertical.flyouts ul {
  display: inline-flex;
}

@media (max-width: 767px) {
  .hs-menu-wrapper.hs-menu-flow-vertical ul {
    display: flex;
  }
}

.hs-menu-wrapper.flyouts .hs-item-has-children {
  position: relative;
}

.hs-menu-wrapper.flyouts .hs-menu-children-wrapper {
  left: -9999px;
  opacity: 0;
  position: absolute;
}

.hs-menu-wrapper.flyouts .hs-menu-children-wrapper a {
  display: block;
  white-space: nowrap;
}

.hs-menu-wrapper.hs-menu-flow-horizontal.flyouts .hs-item-has-children:hover > .hs-menu-children-wrapper {
  left: 0;
  opacity: 1;
  top: 100%;
}

.hs-menu-wrapper.hs-menu-flow-vertical.flyouts .hs-item-has-children:hover > .hs-menu-children-wrapper {
  left: 100%;
  opacity: 1;
  top: 0;
}

@media (max-width: 767px) {
  .hs-menu-wrapper.flyouts .hs-menu-children-wrapper,
  .hs-menu-wrapper.hs-menu-flow-horizontal.flyouts .hs-item-has-children:hover > .hs-menu-children-wrapper,
  .hs-menu-wrapper.hs-menu-flow-vertical.flyouts .hs-item-has-children:hover > .hs-menu-children-wrapper {
    left: 0;
    opacity: 1;
    position: relative;
    top: auto;
  }
}

/* CTA, logo, and rich text images */

.hs_cos_wrapper_type_cta img,
.hs_cos_wrapper_type_logo img,
.hs_cos_wrapper_type_rich_text img {
  height: auto;
  max-width: 100%;
}

/* Utilities
Helper classes with ability to override anything that comes before it
*/

/* For content that needs to be visually hidden but stay visible for screenreaders */

.show-for-sr {
  border: 0 !important;
  clip: rect(0, 0, 0, 0) !important;
  height: 1px !important;
  overflow: hidden !important;
  padding: 0 !important;
  position: absolute !important;
  white-space: nowrap !important;
  width: 1px !important;
}

@media (max-width: 767px) {
  .show-for-sr--mobile {
    border: 0 !important;
    clip: rect(0, 0, 0, 0) !important;
    height: 1px !important;
    overflow: hidden !important;
    padding: 0 !important;
    position: absolute !important;
    white-space: nowrap !important;
    width: 1px !important;
  }
}

/* ==========================================================================
   Glow design system
   Bundled here rather than per-template. Two reasons: get_asset_url in
   layouts/base.html resolves relative to the layout, not the page, so
   per-template paths were silently producing href="" — and at 51KB total the
   whole system costs less than the extra request. Every page therefore has
   every glow class available, which also removes the cross-file dependencies
   (.glow-index lives in _home.css but the treatments index needs it).
   Order matters: tokens first, then base, then chrome, then page layers.
   ========================================================================== */

/* ==========================================================================
   Glow Cosmetique — design tokens
   Single source of truth. Change a value here, it changes everywhere.

   Direction: warm and editorial rather than cool and austere. Serif display,
   soft tinted grounds, deep warm field for contrast. Composition does the
   work — no shadows, no gradients, no decoration.
   ========================================================================== */

:root {
  /* ---- Colour ---------------------------------------------------------
     Warmed from the group palette. Burgundy remains the accent and now
     also anchors the deep field, which ties the site to the clinic's
     own colour rather than a borrowed slate. */
  --glow-cream:      #FAF7F2;   /* page background */
  --glow-blush:      #F4EAE2;   /* tinted card / soft ground */
  --glow-light:      #EFE6DC;   /* secondary surface */
  --glow-border:     #E2D6C9;   /* hairlines */
  --glow-ink:        #241C1A;   /* primary text — warm near-black */
  --glow-ink-soft:   #4A3E39;   /* body text */
  --glow-muted:      #7A6A62;   /* secondary text */
  --glow-deep:       #33211F;   /* deep field — footer, contrast blocks */
  --glow-burgundy:   #6D2932;   /* accent */
  --glow-sage:       #41493E;   /* secondary accent, used rarely */
  --glow-white:      #FFFFFF;

  /* ---- Attention, and ONLY attention -----------------------------------
     Added 3 Aug 2026. Burgundy was doing two jobs — brand accent AND "outside
     the expected range" — so a section heading, a link and a flagged marker all
     read at the same weight on a page carrying 62 rows. Amber now means one
     thing: LOOK HERE. Burgundy goes back to meaning Glow.

     Deliberately no green. Green would mean "good", which is a verdict the
     clinic has not given — and on a hormone panel "in range" is not a pass mark,
     it is a description. Amber is the only signal colour on the page, and its
     worst case is still amber. */
  --glow-flag:       #A8641E;   /* out of range — 4.9:1 on cream */
  --glow-flag-soft:  #F6EBDC;   /* its tint */
  --glow-flag-line:  #E3CBA8;   /* its hairline */

  /* ---- Type -----------------------------------------------------------
     Instrument Serif for display — editorial, high contrast, unhurried.
     Inter for everything functional. */
  --glow-font-display: 'Instrument Serif', Georgia, 'Times New Roman', serif;
  --glow-font-body:    'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  --glow-h1:    clamp(3rem, 7.4vw, 5.5rem);
  --glow-h2:    clamp(2rem, 4.2vw, 3.25rem);
  --glow-h3:    clamp(1.3rem, 2vw, 1.625rem);

  /* ---- The public scale BELOW h3 ----------------------------------------
     ADDED 8 AUGUST 2026, and naming it is the point. The three tokens above
     were the whole public type scale; everything smaller than h3 was written
     as a literal, in six stylesheets, twenty-nine times.

     Same missing-scale fault the portal had — and the same consequence: every
     component invented its own size and none of them agreed. It is invisible
     on the public site in a way it was not in the portal, because editorial
     pages are mostly long-form and the sizes rarely meet.

     ⚠ THIS BLOCK CHANGES NOTHING. Every value is the literal it replaces, to
     the pixel, verified by computed-style diff before and after. It exists so
     the duplication is FINDABLE — you cannot collapse a scale you cannot grep.

     WHAT NAMING IT REVEALED, and what was then done about it — 8 Aug, same
     day, as a SECOND and deliberate pass:

     t2/t3/t4 were 24/22/20px and all three carried titles. Three sizes inside
     4px is the exact fault the portal block below records — a reader cannot
     perceive a 2px step as rank, so it registers as inconsistency instead.

     ONE TITLE SIZE NOW: t3, 22px. Moved down from 24 — .glow-index__title,
     .glow-step__name (home and page), .glow-pricetable__group th. Moved up
     from 20 — .glow-product__title, .glow-cart-line__title. Six declarations,
     and they were the whole disagreement.

     ⚠ PRICES WERE DELIBERATELY LEFT ALONE. Hemi: *"prices are a different job
     and a real rank; titles aren't."* He is right, and the hierarchy that
     survives is legible precisely because the titles stopped competing with
     it: 28px cart total > 24px product hero price > 20px line items. A number
     that is bigger because it is the total is rank. A heading that is bigger
     because of which stylesheet it landed in is not. */
  --glow-t1:    1.75rem;    /* 28px — the cart total. The largest number on the
                               site, and the only thing at this size */
  --glow-t2:    1.5rem;     /* 24px — the product hero price */
  --glow-t3:    1.375rem;   /* 22px — THE title size. Every card, section and
                               product title on the public site */
  --glow-t4:    1.25rem;    /* 20px — line-item prices, and the FAQ marker */
  --glow-t5:    1.125rem;   /* 18px */
  --glow-fine:  0.8125rem;  /* 13px — sits between --glow-small (14) and
                               --glow-meta (12) and belongs to neither, which
                               is the tell. Named so it can be retired. */

  --glow-lead:  clamp(1.0625rem, 1.5vw, 1.1875rem);
  --glow-body:  1.0625rem;
  --glow-small: 0.875rem;   /* 14px — the portal's workhorse */
  --glow-meta:  0.75rem;    /* 12px — genuinely secondary text */
  --glow-tag:   0.6875rem;  /* 11px — UPPERCASE eyebrows only; caps read larger */

  /* ---- THE EYEBROW — one definition, both surfaces ----------------------
     ADDED 8 AUGUST 2026. Hemi: make the site go with the portal.

     The eyebrow is the small uppercase label above a card or section. It is
     the most repeated element on either surface and the most recognisable
     brand tic — so having the portal render ONE treatment while the public
     site rendered FIVE was the largest remaining seam between them.

     Public site before: 11px/500/0.09em (five components), 11px/500/0.14em
     (footer heading). Portal: 11px/600/0.11em. Now both read these tokens.

     ⚠ TO REVERT, change the two values below and nothing else. Every eyebrow
     on both surfaces resolves through them, so the whole change is two lines:
         --glow-eyebrow-weight: 500;
         --glow-eyebrow-track:  0.09em;
     That is the pre-8-Aug public treatment. (The commit is isolated too, so
     `git revert` works — but this is the one that needs no tooling.)

     NOT eyebrows, deliberately left alone: .glow-hero__meta and
     .glow-pagehead__meta. They read "A plan, before a treatment." and
     "Included, not an add-on · Registered nurse-led · Springvale" — sentences
     and fact strings, not labels. They stay at --glow-fine/0.05em. A label
     names the thing below it; a byline says something. Different jobs. */
  --glow-eyebrow-size:   var(--glow-tag);   /* 11px */
  --glow-eyebrow-weight: 600;               /* was 500 on the public site */
  --glow-eyebrow-track:  0.11em;            /* was 0.09em (and 0.14em once) */

  /* ---- Portal type ------------------------------------------------------
     ADDED 5 AUGUST 2026. Hemi, comparing the portal with SigmaSync's client
     portal: *"the multiple text sizes and positioning is still very messy."*

     He was right, and it was measurable. The overview rendered TWELVE distinct
     font sizes — 10, 11, 12, 13, 14, 15, 16, 17–19, 20, 28px — and two <h2>s
     at the same nesting level rendered at 12px and 20px. `_portal.css` carried
     fifteen hardcoded sizes with no token behind any of them, including
     `0.9375rem` used ten times as a de-facto row size that exists between
     --glow-small and --glow-body and belongs to neither.

     THE CAUSE WAS A MISSING SCALE, NOT CARELESSNESS. The tokens above are the
     PUBLIC scale: --glow-h1 tops out at 88px, which no portal page could ever
     use. So every portal component invented its own size, each one reasonable
     alone and none of them agreeing.

     Four tokens close it. Every portal component maps onto one of these plus
     --glow-body / --glow-small / --glow-meta, which takes the portal from
     twelve rendered sizes to seven.

     Named `pt-` rather than added to the scale above because the public site
     must not inherit them: a 1.25rem "card title" would be wrong on a page
     whose h2 is 52px. Two surfaces, two scales, stated once. */
  --glow-pt-h1:    1.75rem;    /* 28px — the portal page title, and the largest
                                  thing on any portal page. AFTER 8 AUG (below)
                                  it is also the ONLY serif on a portal page,
                                  apart from the rail wordmark and the archetype
                                  identity panel. */
  --glow-pt-title: 1rem;       /* 16px, Inter 600 — every card, tile and section
                                  title.

     ADDED 8 AUGUST 2026, REPLACING --glow-pt-card (20px serif). Hemi, with the
     Inside Out course and the SigmaSync portal open beside this one: *"Inside
     Out looks tight and good. SigmaSync looks tight and good. But there is
     something about Glow portal that looses the balance between fonts and can
     just look messy."*

     The something was measurable: both references are ONE-typeface sans systems
     where weight carries rank; this portal interleaved 20px Instrument Serif
     with Inter at close range — eight components set serif titles INSIDE sans
     body contexts, with three different letter-spacings on visually identical
     titles. Each was fine alone. Together they were the mess.

     The ruling: serif survives at the page h1 and the rail wordmark, nowhere
     else. Titles are Inter 600 at the same size as prose — one weight step up,
     which is how both references do hierarchy.

     NOT 15px: at 15/600 beside --glow-pt-row 15/500, the two ranks would
     differ by weight alone at identical size, which is exactly the
     imperceptible-step fault this file keeps recording. */
  --glow-pt-row:   0.9375rem;  /* 15px — a row title or a stated value. The
                                  size that was already doing this job ten
                                  times over without a name */
  --glow-pt-value: 1.125rem;   /* 18px — the stated analyte value, the one
                                  number a metric row exists to show. Was a
                                  literal on .glow-metric__v b */
  --glow-pt-label: 0.6875rem;  /* 11px — THE uppercase eyebrow. One size, 600,
                                  0.11em. There were six variants: 10–12px,
                                  tracking 0.06–0.18em, three colours */
  --glow-pt-prose: 0.875rem;   /* 14px — READING PROSE anywhere in the portal.

     STEPPED 16px -> 14px, 30 August 2026, AND THIS CORRECTS THE 8 AUG RULING
     ABOVE. Hemi: "the large font doesn't attract the eye... at SS they all
     stand well and are read throughout the page. The eye doesn't dart like it
     does Glow."

     The 8 Aug note says titles sit "at the same size as prose — one weight step
     up, which is how both references do hierarchy". That was measured wrong.
     Counting SigmaSync's portal directly: text-xs (12px) 434 uses, text-sm
     (14px) 347, text-base (16px) ELEVEN — and every one of those eleven is a
     font-semibold card title.

     So SS does not carry rank by weight at one size. Its BASELINE is 12-14px,
     which is what leaves a 16px semibold title room to read as large. Glow set
     baseline and title both at 16px, so nothing on a portal page was ever
     bigger than the body text — and a title that is never bigger than the
     paragraph under it cannot attract the eye. Everything was large, so
     nothing was.

     Lower the floor rather than raise the ceiling: --glow-pt-title stays 16/600
     and finally has room to work. Leading and measure tightened with it —
     16px at 1.65 across 68ch was ~90 characters a line inside a card whose
     labels are 11px.

     ADDED 7 AUGUST 2026. The 5 Aug pass above normalised titles, rows, labels
     and eyebrows and stopped there — it never touched paragraphs, and three
     sizes were left doing the same job: --glow-body 17px in .glow-card__body p,
     a literal 1rem in .glow-slead, and --glow-pt-row 15px in .glow-relby__p
     and .glow-caution p. Hemi, looking at three of them stacked in one card:
     *"When you use this big font it looks sloppy on all the pages its used."*

     17px was chosen deliberately — "a step LARGER than the surrounding UI" —
     and the reasoning was sound in isolation. It is wrong at this density: a
     results page is mostly short paragraphs between data, and prose set two
     steps above the 14px workhorse reads as a different document interleaved
     with the first.

     16px keeps a clear step over --glow-small without shouting, and it is one
     size instead of three.

     8 AUG: the portal lead also collapses onto this token. It was inheriting
     the PUBLIC --glow-lead clamp (17–19px, viewport-dependent) — the largest
     body text in the portal, on every page, one step above prose for no reason
     a reader could name. */

  /* Portal surfaces that had no name — both were literals in _portal.css. */
  --glow-pt-ground: #F2EEE7;   /* the content-column ground behind the cards */
  --glow-pt-well:   #FAF8F5;   /* the flush-block well */

  /* The vertical inset on every header row — .glow-block__head,
     .glow-card__head and .glow-sec > summary. They were 1.05rem, 1.5rem and
     1.15rem: three near-identical values that stack visibly when a card sits
     inside a block, which is the normal case. */
  --glow-head-y: 1.05rem;

  /* ---- Space ----------------------------------------------------------
     THE MISSING RUNGS, added 30 August 2026.

     The scale started at --glow-s1 (8px) and had nothing below it, so every
     micro-space in the system was invented by hand: 0.125, 0.25, 0.375, 0.45,
     0.625, 0.65, 0.7, 0.75, 0.85, 0.9rem — ninety-three distinct literal
     spacing values across css/glow/, with several pairs within 1-2px of each
     other. In the intake form alone there were eight different vertical gaps:
     2, 4, 6, 8, 10, 12, 16, 24px.

     Gaps two pixels apart do not read as different ranks — they read as noise,
     and the eye cannot group what it cannot rank. Hemi, looking at the form:
     "tell me about the spacings?"

     ⚠ THIS IS THE SAME FAULT THIS FILE ALREADY RECORDS TWICE FOR TYPE — at the
     8 Aug type-scale pass and again at the eyebrow unification, both of which
     concluded "a missing scale, not carelessness". It was diagnosed correctly
     for font-size and never once for space. Now it is on a 4px grid:
     4 · 8 · 12 · 16 · 24 · 32 · 48 · 72 · 112 · 160. */
  --glow-s0:  0.25rem;   /* 4px  — the rung that never existed */
  --glow-s1:  0.5rem;
  --glow-s1h: 0.75rem;   /* 12px — the other one, between s1 and s2 */
  --glow-s2:  1rem;
  --glow-s3:  1.5rem;
  --glow-s4:  2rem;
  --glow-s5:  3rem;
  --glow-s6:  4.5rem;
  --glow-s7:  7rem;
  --glow-s8:  10rem;

  --glow-section-y: clamp(4rem, 9vw, 8rem);

  /* ---- Split-header offset --------------------------------------------
     9 Aug 2026. `.glow-split` top-aligned both columns, so the right-hand
     paragraph's first line landed level with the EYEBROW PILL rather than
     with the h2. An 11px uppercase label and a 17px paragraph then shared a
     baseline zone and the eye could not tell which one started the block —
     Hemi, on the home page: *"does this seem the correct balance?"*

     Pushing the right column down by the eyebrow's own block size makes the
     eyebrow read as a label over the WHOLE split, and puts the two pieces of
     real content level. Derived from the eyebrow tokens rather than typed as
     a number, so restyling the pill cannot silently break the alignment:

       line box   11px × 1.7 line-height
       padding    0.45rem × 2   (.glow-tag)
       margin     --glow-s4     (.glow-tag margin-bottom)

     ⚠ NO OPTICAL CORRECTION TERM, AND THAT IS THE MEASURED ANSWER, NOT AN
     OMISSION. I predicted one was needed: the h2 runs at line-height 1.1 and
     body prose at 1.7, so the paragraph carries far more half-leading above
     its first line and should have sat visibly low. Reasoning from that, I
     added −0.35rem.

     Measured in Chrome at 1440px — canvas font metrics for true cap-height,
     across all five splits on the home page — the raw box-model match already
     lands the two CAP-TOPS within **1.0px**, and the correction pushed the
     paragraph 6.6px too HIGH. The prediction was wrong in direction, not just
     in size, because the eyebrow's own line box absorbs the difference.

     The lesson is the repo's recurring one: an argument that sounds right is
     not a measurement. Re-measure — do not re-derive — if --glow-h2, the body
     line-height or .glow-tag's padding moves. */
  --glow-split-offset: calc(
    (var(--glow-tag) * 1.7) + 0.9rem + var(--glow-s4)
  );

  /* ---- Measure --------------------------------------------------------- */
  --glow-container: 1280px;
  --glow-measure:   62ch;
  --glow-gutter:    clamp(1.25rem, 5vw, 3.5rem);

  /* ---- Radius ----------------------------------------------------------
     ONE BASE, EVERYTHING DERIVED. Ported from SigmaSync 30 Aug 2026, where
     `--radius: 0.5rem` and every other step is a calc() from it — which is why
     that system cannot drift. Change the base and the whole scale moves.

     Three values, each with a stated job. There is no fourth, and no literal
     radius may appear anywhere in css/glow/ — scripts/qa_gate.py fails on one.
     See sop/build/B-08-the-component-contract.md §2. */
  --glow-radius-ui: 8px;                              /* functional: buttons, cards, fields, rows */
  --glow-radius:    calc(var(--glow-radius-ui) * 1.75); /* 14px — editorial marketing cards */
  --glow-radius-sm: 999px;                            /* chips, tags, counters, stepper shells */

  /* ---- THE LINE --------------------------------------------------------
     The page inset, tokenised 30 Aug 2026. It was a bare clamp() inside
     .glow-portal ground padding, while card bodies inset --glow-s3 and the
     card header carried its own copy of that number under a comment reading
     "⚠ MUST MATCH .glow-block__body's inset". Two rules that must agree have
     to SHARE A VARIABLE — a comment is not a constraint, it is a bug that has
     not happened yet, and it is the same failure that copied the button
     geometry into four files.

     Two sanctioned x-positions per page and only two:
       PAGE LINE    = --glow-page-pad from the viewport edge
       CONTENT LINE = PAGE LINE + --glow-card-pad
     Nothing renders at any other x. B-08 §0. */
  --glow-page-pad: clamp(1.25rem, 3vw, 2rem);
  --glow-card-pad: var(--glow-s3);

  /* ---- Controls ---------------------------------------------------------
     One field, one button pad. Before 30 Aug 2026 the four text inputs on this
     site rendered at FOUR different radii — a 999px stadium pill on /contact,
     a 2px near-square in the cart, and 14px in intake and auth — for one
     conceptual element. The token doc had already assigned fields to
     --glow-radius-ui; none of them obeyed it, because nothing checked. */
  --glow-field-pad:  0.75rem 0.9rem;
  --glow-ui-pad:     0.9rem 1.75rem;    /* standard button */
  --glow-ui-pad-sm:  0.5rem 1rem;       /* compact button */

  /* ---- ROLES -----------------------------------------------------------
     THE PALETTE ABOVE IS NOT FOR COMPONENTS. Components use the roles below.

     This is the load-bearing pillar taken from SigmaSync, whose components
     reference --card / --border / --muted-foreground and never a colour name.
     When a component picks from the palette, every author makes a fresh
     decision and the decisions drift — fifteen button treatments, three
     borders, a different muted grey per file. When it picks a role, the
     decision was made once, here.

     Migrate components to these as they are touched. New rules must use them;
     the gate enforces it. B-08 §1. */
  --glow-ground:         var(--glow-cream);      /* page background */
  --glow-surface:        var(--glow-white);      /* card / module background */
  --glow-surface-alt:    var(--glow-blush);      /* tinted surface, selected state */
  --glow-surface-border: var(--glow-border);     /* THE hairline. There is one. */
  --glow-text:           var(--glow-ink);        /* primary */
  --glow-text-soft:      var(--glow-ink-soft);   /* body */
  --glow-text-muted:     var(--glow-muted);      /* labels, meta, eyebrows */
  --glow-action:         var(--glow-burgundy);   /* the action colour */
  --glow-action-ink:     var(--glow-cream);      /* what sits on the action colour */
  --glow-action-hover:   var(--glow-deep);
  --glow-state-tint:     var(--glow-blush);      /* hover / selected fill */

  /* ---- Motion ---------------------------------------------------------- */
  --glow-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --glow-dur:  380ms;
}

@media (prefers-reduced-motion: reduce) {
  :root { --glow-dur: 1ms; }
}
/* ==========================================================================
   Glow Cosmetique — base typography and layout primitives
   ========================================================================== */

html { -webkit-text-size-adjust: 100%; }

body {
  background: var(--glow-cream);
  color: var(--glow-ink-soft);
  font-family: var(--glow-font-body);
  font-size: var(--glow-body);
  line-height: 1.7;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4 {
  font-family: var(--glow-font-display);
  color: var(--glow-ink);
  font-weight: 400;
  letter-spacing: -0.015em;
  line-height: 1.06;
  margin: 0;
}

h1 { font-size: var(--glow-h1); }
h2 { font-size: var(--glow-h2); line-height: 1.1; }
h3 { font-size: var(--glow-h3); line-height: 1.25; letter-spacing: -0.005em; }

p { margin: 0 0 var(--glow-s3); max-width: var(--glow-measure); }
p:last-child { margin-bottom: 0; }

a { color: inherit; text-decoration: none; }

/* ---- Layout ------------------------------------------------------------ */

.glow-container {
  width: 100%;
  max-width: var(--glow-container);
  margin-inline: auto;
  padding-inline: var(--glow-gutter);
}

.glow-section { padding-block: var(--glow-section-y); }
.glow-section--tight { padding-block: clamp(3rem, 6vw, 5rem); }
.glow-section--line { border-top: 1px solid var(--glow-border); }

/* ---- Split header -------------------------------------------------------
   Eyebrow + h2 on the left, supporting prose on the right. The site's most
   reused block — home, treatments, concerns, comparisons, approach.

   ⚠ MOVED HERE 9 AUG 2026 AND IT WAS DECLARED TWICE. Identical copies sat in
   _page.css:606 and _home.css:76 with a "keep in sync" comment, which is a
   note saying the bug is known rather than a mechanism preventing it. It
   worked only because _home.css loads second and won. It is a layout
   primitive, so it belongs with .glow-container, and there is now one of it.

   The right column is pushed down so its first line sits with the h2's cap
   line rather than the eyebrow's — see --glow-split-offset for why, and for
   why the last term in it was measured rather than derived.

   `:has()` guards it: a split with no eyebrow needs no offset, and there are
   several. Without the guard those columns would drop 65px for nothing. */

.glow-split { display: grid; gap: var(--glow-s5); }

@media (min-width: 920px) {
  .glow-split {
    grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
    gap: var(--glow-s7);
    align-items: start;
  }
  .glow-split > :first-child:has(> .glow-tag) ~ * {
    padding-block-start: var(--glow-split-offset);
  }
}

/* ---- Tag ---------------------------------------------------------------
   Small pill label. Names the section without shouting. Replaces the
   letterspaced eyebrow — softer, and it reads as a label rather than a
   heading fragment. */

.glow-tag {
  display: inline-block;
  font-family: var(--glow-font-body);
  font-size: var(--glow-eyebrow-size);
  font-weight: var(--glow-eyebrow-weight);
  letter-spacing: var(--glow-eyebrow-track);
  text-transform: uppercase;
  color: var(--glow-muted);
  background: var(--glow-blush);
  border-radius: var(--glow-radius-sm);
  padding: 0.45rem 0.9rem;
  margin: 0 0 var(--glow-s4);
}

.glow-tag--onDeep {
  background: rgba(250, 247, 242, 0.1);
  color: rgba(250, 247, 242, 0.72);
}

/* ---- Lead paragraph ---------------------------------------------------- */

.glow-lead {
  font-size: var(--glow-lead);
  line-height: 1.68;
  color: var(--glow-ink-soft);
  max-width: 48ch;
}

/* ---- Button -------------------------------------------------------------
   Primary CTA. "Book a consultation" — trained interface language, decided
   30 Jul 2026 (see notes/decisions.md). Filled, calm, no shouting. */

.glow-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  font-family: var(--glow-font-body);
  font-size: var(--glow-small);
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--glow-cream);
  background: var(--glow-burgundy);
  border: 1px solid var(--glow-burgundy);
  border-radius: var(--glow-radius-ui);
  padding: 0.9rem 1.75rem;
  /* Explicit line-height: <a> inherited body 1.7 and <button> normalize 1.15,
     so the same class rendered 54.6px and 46.9px. One number, one height. */
  line-height: 1.2;
  white-space: nowrap;
  transition: background var(--glow-dur) var(--glow-ease),
              border-color var(--glow-dur) var(--glow-ease);
}

.glow-button:hover,
.glow-button:focus-visible {
  background: var(--glow-deep);
  border-color: var(--glow-deep);
}

.glow-button--outline {
  color: var(--glow-ink);
  background: transparent;
  border-color: var(--glow-border);
}

.glow-button--outline:hover,
.glow-button--outline:focus-visible {
  background: var(--glow-blush);
  border-color: var(--glow-border);
}

/* ---- Availability line -------------------------------------------------
   Retained as a quiet secondary CTA. No longer the only permitted form. */


.glow-link {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: var(--glow-small);
  color: var(--glow-burgundy);
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
  transition: border-color var(--glow-dur) var(--glow-ease);
}

.glow-link:hover { border-color: var(--glow-burgundy); }

/* ---- Focus / skip ------------------------------------------------------- */

:focus-visible { outline: 2px solid var(--glow-burgundy); outline-offset: 3px; }

.glow-skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--glow-deep);
  color: var(--glow-cream);
  padding: var(--glow-s2) var(--glow-s3);
  z-index: 200;
}
.glow-skip:focus { left: 0; }

/* ---- Italic emphasis in display type ------------------------------------
   Aestetika's signature move: roman and italic mixed inside one heading.
   Instrument Serif's italic is markedly different from its roman, so the
   contrast reads as intent rather than accident. Use on one phrase only. */

.glow-em {
  font-style: italic;
  letter-spacing: 0;
}

/* ---- Section divider ----------------------------------------------------
   Oversized type as a rule. Adds scale to a page that, by doctrine, carries
   less content than a competitor's.

   It was --glow-blush (#F4EAE2) on --glow-cream (#FAF7F2): a contrast ratio of
   about 1.06:1, which is not "subtle" — it is invisible, and on a good screen
   it read as a rendering fault rather than a decision. Hemi, 31 Aug 2026:
   "this light coloured font doesnt work."

   It now carries the line properly at --glow-muted (4.84:1 on cream). It is
   the brand's own sentence, so it is worth reading; the restraint is in the
   tone and in saying it once, not in hiding it. */

.glow-divider {
  overflow: hidden;
  border-top: 1px solid var(--glow-border);
  padding-block: clamp(2rem, 5vw, 4rem);
}

.glow-divider__text {
  font-family: var(--glow-font-display);
  font-size: min(8vw, 100px);
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--glow-muted);
  white-space: nowrap;
  margin: 0;
  user-select: none;
}

/* ---- Index rows ---------------------------------------------------------
   Title, description, arrow. A directory that points rather than sells. */

.glow-index { border-top: 1px solid var(--glow-border); }

.glow-index__row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--glow-s2) var(--glow-s4);
  align-items: center;
  padding-block: var(--glow-s4);
  border-bottom: 1px solid var(--glow-border);
  transition: padding-inline var(--glow-dur) var(--glow-ease);
}

/* MOBILE, fixed 6 Aug 2026. The base rule is `1fr auto` for THREE children,
   so title took column 1, description took column 2, and the arrow wrapped to
   a row of its own. On a 390px phone that squeezed the title to ~160px —
   "Peptide facial vs glass skin facial" set five lines deep in display face
   beside a half-empty description column, with a stray arrow underneath.

   Title and description belong in one column, stacked, reading top to bottom.
   The arrow spans both rows so it stays put and stops generating a third row
   of its own. */
.glow-index__title { grid-column: 1; }
.glow-index__desc  { grid-column: 1; }
.glow-index__arrow { grid-column: 2; grid-row: 1 / -1; align-self: center; }

@media (min-width: 780px) {
  .glow-index__row { grid-template-columns: 16rem 1fr auto; gap: var(--glow-s5); }
  .glow-index__row:hover { padding-inline: var(--glow-s2); }
  .glow-index__title { grid-column: 1; }
  .glow-index__desc  { grid-column: 2; }
  .glow-index__arrow { grid-column: 3; grid-row: auto; }
}

.glow-index__title {
  font-family: var(--glow-font-display);
  font-size: var(--glow-t3);
  color: var(--glow-ink);
  line-height: 1.2;
}

.glow-index__desc {
  font-size: var(--glow-small);
  color: var(--glow-muted);
  margin: 0;
  max-width: 56ch;
}

.glow-index__arrow {
  color: var(--glow-burgundy);
  font-size: var(--glow-t5);
  transition: transform var(--glow-dur) var(--glow-ease);
}

.glow-index__row:hover .glow-index__arrow { transform: translateX(5px); }

/* The way out of a price list. Added 9 Aug 2026: the index ended on a number
   with no exit but a treatment page, at the exact point a reader is weighing
   cost. The note sits beside the button rather than under it — it is a reason,
   not a disclaimer, and stacking it reads as small print. */
.glow-index__cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--glow-s3);
  margin-top: var(--glow-s5);
  max-width: none;   /* p carries --glow-measure; this one is a row */
}

.glow-index__ctanote {
  font-size: var(--glow-small);
  color: var(--glow-muted);
  max-width: 40ch;
}

/* ---- Form control accent ------------------------------------------------
   One line, system-wide. Checkboxes and radios rendered in the browser's
   default blue everywhere except the intake form, which set this on 8 Aug and
   was the only place that had. B-08 §8 — state is defined once, for everything. */
input[type="checkbox"],
input[type="radio"] { accent-color: var(--glow-action); }

/* Visually hidden, present to assistive tech. Added 31 Aug 2026 for the
   portal shop's favourite announcements; generic on purpose. */
.glow-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
/* ==========================================================================
   Glow Cosmetique — header and footer
   ========================================================================== */

.body-wrapper { display: flex; flex-direction: column; min-height: 100vh; }

/* ---- Header ------------------------------------------------------------ */

.glow-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: color-mix(in srgb, var(--glow-cream) 90%, transparent);
  backdrop-filter: saturate(150%) blur(14px);
  border-bottom: 1px solid var(--glow-border);
}

.glow-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--glow-s4);
  min-height: 78px;
}

.glow-wordmark {
  font-family: var(--glow-font-display);
  font-size: var(--glow-t3);
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--glow-ink);
  white-space: nowrap;
}

.glow-nav { display: flex; align-items: center; gap: clamp(1.25rem, 2.4vw, 2.25rem); }

.glow-nav__link {
  font-size: var(--glow-small);
  letter-spacing: 0.01em;
  white-space: nowrap;
  color: var(--glow-ink-soft);
  padding-block: 0.35rem;
  border-bottom: 1px solid transparent;
  transition: color var(--glow-dur) var(--glow-ease),
              border-color var(--glow-dur) var(--glow-ease);
}

.glow-nav__link:hover,
.glow-nav__link[aria-current="page"] {
  color: var(--glow-ink);
  border-color: var(--glow-border);
}

.glow-header__cta { display: none; white-space: nowrap; }

/* ── The action cluster ──────────────────────────────────────────────────────
   The nav lays every child out on ONE flex gap, so the phone sat as far from
   "About" as it did from the CTA. Equidistant means grouped with neither, and
   that is why it read as lost however it was styled — three attempts at the
   pill itself changed nothing, because the pill was never the problem.

   Wrapping the two actions gives them their own tighter gap, and the nav's
   larger gap then falls BEFORE the cluster instead of through the middle of
   it. Proximity does the work no amount of border-radius could. */
.glow-nav__actions {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* The handset. Sized against the number rather than the pill, and nudged up a
   hair because a glyph's optical centre sits below its box's. */
.glow-nav__phone-mark {
  flex: none;
  margin-top: -1px;
  opacity: 0.75;
  transition: opacity var(--glow-dur) var(--glow-ease);
}
.glow-nav__phone:hover .glow-nav__phone-mark { opacity: 1; }

/* Phone in the chrome — for a local clinic this converts better than any
   design element. Hidden mid-width so the nav never wraps.

   ⚠ REWORKED TWICE, 21 Aug 2026, and the second attempt is the one that holds.

   It began at nav size and weight 500 — the same visual weight as "FAQ" —
   which made the highest-converting element in the chrome the quietest thing
   in it. Setting it as display type fixed the weight and not the problem:
   Hemi, on seeing it, "the phone number is still rubbish". He was right. Type
   alone gave it presence but no AFFORDANCE, so it read as neither navigation
   nor action, floating between the two.

   It is now the OUTLINED half of a pair with the solid CTA. That is what fixes
   it: not size, but shape. It takes the same form as the thing beside it, so
   the eye reads two actions with an obvious hierarchy — ring us, or book —
   instead of a number adrift near a button.

   ⚠ AND THIS SUPERSEDES THE NOTE I WROTE HERE YESTERDAY arguing against a
   second button because "two buttons split the primary action". That is true
   of two buttons of EQUAL weight. Outlined against solid is a hierarchy, not a
   split, and it is the standard pattern precisely because it reads instantly. */
.glow-nav__phone {
  display: none;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--glow-font-body);
  font-size: var(--glow-small);
  font-weight: 500;
  letter-spacing: 0.01em;
  white-space: nowrap;
  color: var(--glow-ink);
  background: transparent;
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius-ui);
  line-height: 1.2;
  /* Vertical padding MATCHES .glow-button exactly (0.9rem) so the two sit at
     the same height — a pair at different heights reads as a mistake, not a
     hierarchy. Horizontal is tighter because the number is a short string and
     1.75rem around it would look padded out. */
  padding: 0.9rem 1.35rem;
  transition: border-color var(--glow-dur) var(--glow-ease),
              background var(--glow-dur) var(--glow-ease);
}

.glow-nav__phone:hover,
.glow-nav__phone:focus-visible {
  border-color: var(--glow-ink);
  background: var(--glow-blush);
}

@media (min-width: 1140px) {
  .glow-nav__phone { display: inline-flex; }
}

/* ---- Cart link + count badge --------------------------------------------
   The count is written by js/cart.js into #glow-cart-count and hidden at
   zero, so the pill only appears once something is actually in the cart. */

.glow-nav__cart {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}

.glow-cart-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.3em;
  padding: 0.18em 0.45em;
  font-size: var(--glow-tag);
  font-weight: 500;
  line-height: 1;
  color: var(--glow-cream);
  background: var(--glow-burgundy);
  border-radius: var(--glow-radius-sm);
}

/* display:inline-flex would defeat the hidden attribute — restate it. */
.glow-cart-count[hidden] { display: none; }

/* ---- MOBILE CART BUTTON — added 31 Aug 2026 ----------------------------
   Sits in the header bar, OUTSIDE .glow-nav, so it survives the nav becoming
   a drawer below 1040px. The nav's own Cart link is hidden at those widths so
   the two never both show.

   Tap target is 44px — Apple's minimum and the reason this is padded rather
   than sized to the 19px icon. A cart you have to aim at is a cart you miss. */
.glow-cartbtn {
  display: inline-flex; align-items: center; justify-content: center;
  position: relative;
  width: 44px; height: 44px; margin-inline-end: calc(var(--glow-s0) * -1);
  color: var(--glow-ink); text-decoration: none;
}
.glow-cartbtn:hover { color: var(--glow-burgundy); }
.glow-cartbtn .glow-cart-count {
  position: absolute; top: 4px; right: 2px;
}

/* The drawer's booking button. Full width like every other drawer row, and
   it is a button rather than a link because it is the one action in there. */
.glow-nav__book { display: none; }

.glow-burger {
  display: inline-flex; flex-direction: column; justify-content: center;
  gap: 5px; width: 30px; height: 30px; padding: 0; border: 0;
  background: none; cursor: pointer;
}
.glow-burger span {
  display: block; height: 1px; width: 100%; background: var(--glow-ink);
  transition: transform var(--glow-dur) var(--glow-ease), opacity var(--glow-dur) var(--glow-ease);
}
.glow-burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.glow-burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.glow-burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* ⚠ 940px WAS TOO EARLY AND IT OVERFLOWED THE PAGE. Fixed 9 Aug 2026.
   The burger switched off and the header CTA switched on at 940, but the
   full nav plus that button does not fit until roughly 1000. Between 921 and
   999 the header ran up to 40px past the viewport and the WHOLE SITE scrolled
   sideways — every page, not just the home page. Measured across 880–1120 in
   20px steps: clean to 920, 40px over at 940, 25 at 960, 10 at 980, clean
   again from 1000.

   1040, not 1000. At 1000 the nav's right edge lands 5px short of the
   viewport, which is not a margin — it is a coincidence that any font
   fallback or nav-label edit would eat. At 1040 there is 35px of real slack.

   Found while verifying an unrelated change. It had been live the whole time
   because nobody tests the band between "laptop" and "tablet" — 390, 768 and
   1440 all pass. Worth adding that band to the screenshot list. */
@media (min-width: 1040px) {
  .glow-burger { display: none; }
  .glow-header__cta { display: inline-flex; }
  .glow-cartbtn { display: none; }   /* the nav carries Cart at this width */
}

/* Must stay the exact complement of the rule above — a gap between them
   leaves the nav neither a drawer nor a bar. */
@media (max-width: 1039px) {
  .glow-nav {
    position: fixed; inset: 78px 0 auto 0;
    flex-direction: column; align-items: flex-start; gap: 0;
    background: var(--glow-cream);
    border-bottom: 1px solid var(--glow-border);
    padding: var(--glow-s2) var(--glow-gutter) var(--glow-s5);
    transform: translateY(-8px); opacity: 0; pointer-events: none;
    transition: opacity var(--glow-dur) var(--glow-ease), transform var(--glow-dur) var(--glow-ease);
  }
  .glow-nav[data-open="true"] { opacity: 1; transform: none; pointer-events: auto; }
  .glow-nav__link {
    width: 100%; font-size: var(--glow-t5); padding-block: var(--glow-s3);
    border-bottom: 1px solid var(--glow-border);
  }
  .glow-nav .glow-availability { margin-top: var(--glow-s4); }

  /* One cart badge in the DOM at a time — the bar's copy is the visible one. */
  .glow-nav__cart { display: none; }

  .glow-nav__book {
    display: inline-flex; justify-content: center;
    width: 100%; margin-top: var(--glow-s3); border-bottom: 0;
  }
}

/* ---- Footer ------------------------------------------------------------
   Deep warm field. Closes the page with weight, and carries the wordmark
   at scale — the one place the brand is allowed to be large. */

.glow-footer {
  margin-top: auto;
  background: var(--glow-deep);
  color: rgba(250, 247, 242, 0.72);
  padding-top: clamp(3.5rem, 7vw, 6rem);
  overflow: hidden;
}

.glow-footer__grid {
  display: grid;
  gap: var(--glow-s5);
  grid-template-columns: 1fr;
  padding-bottom: var(--glow-s6);
}

@media (min-width: 780px) {
  .glow-footer__grid { grid-template-columns: 1.5fr 1fr 1fr; gap: var(--glow-s6); }
}

.glow-footer .glow-wordmark { color: var(--glow-cream); }

.glow-footer__heading {
  font-family: var(--glow-font-body);
  font-size: var(--glow-eyebrow-size);
  font-weight: var(--glow-eyebrow-weight);
  letter-spacing: var(--glow-eyebrow-track);
  text-transform: uppercase;
  color: rgba(250, 247, 242, 0.45);
  margin: 0 0 var(--glow-s3);
}

.glow-footer__list { list-style: none; margin: 0; padding: 0; }
.glow-footer__list li {
  margin-bottom: 0.7rem;
  font-size: var(--glow-small);
  color: rgba(250, 247, 242, 0.75);
}
.glow-footer__list a {
  font-size: var(--glow-small);
  color: rgba(250, 247, 242, 0.75);
  border-bottom: 1px solid transparent;
  transition: color var(--glow-dur) var(--glow-ease), border-color var(--glow-dur) var(--glow-ease);
}
.glow-footer__list a:hover { color: var(--glow-cream); border-color: rgba(250, 247, 242, 0.4); }

.glow-footer__note {
  font-size: var(--glow-small);
  color: rgba(250, 247, 242, 0.6);
  max-width: 38ch;
  margin-top: var(--glow-s3);
}

.glow-footer__base {
  display: flex; flex-wrap: wrap;
  gap: var(--glow-s2) var(--glow-s4);
  justify-content: space-between; align-items: baseline;
  padding-block: var(--glow-s3);
  border-top: 1px solid rgba(250, 247, 242, 0.14);
  font-size: var(--glow-fine);
  color: rgba(250, 247, 242, 0.5);
}
.glow-footer__base a { border-bottom: 1px solid transparent; }
.glow-footer__base a:hover { border-color: rgba(250, 247, 242, 0.4); }

/* Build credit sits quieter than the legal links and takes its own line on
   narrow widths so the base row never crowds. */
.glow-footer__credit { color: rgba(250, 247, 242, 0.38); }
.glow-footer__credit a { color: rgba(250, 247, 242, 0.55); }
.glow-footer__credit a:hover { color: var(--glow-cream); }

@media (max-width: 779px) {
  .glow-footer__base { flex-direction: column; }
}

/* The large wordmark, sized to sit edge to edge inside the container.
   "Glow Cosmetique" is ~6.9em wide in Instrument Serif, so the cap keeps it
   from overflowing once the container stops growing. */
.glow-footer__mark {
  font-family: var(--glow-font-display);
  font-weight: 400;
  color: var(--glow-cream);
  font-size: min(12.6vw, 168px);
  line-height: 0.84;
  letter-spacing: -0.025em;
  padding-top: var(--glow-s4);
  padding-bottom: 0.08em;
  white-space: nowrap;
}

/* Members-only portal link. Tinted so it reads as a different KIND of thing to
   the marketing nav beside it — a way back into your own record, not another
   page to browse. Only rendered when signed in; see partials/header.html. */
/* ⚠ DE-TINTED 21 Aug 2026. The blush pill put a THIRD filled element in the
   right-hand cluster — portal pill, phone, burgundy CTA — and at a glance it
   read as a second button competing with the one that matters.

   It still needs to say "different kind of thing", and now does it with a mark
   instead of a fill: a small burgundy dot, the way an account or status
   indicator works. Quieter than the nav links it sits beside, which is correct
   — it is for the few people signed in, and it should be findable rather than
   advertised. */
.glow-nav__portal {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  color: var(--glow-burgundy);
  padding-block: 0.35rem;
}

.glow-nav__portal::before {
  content: "";
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

.glow-nav__portal:hover { color: var(--glow-ink); }

/* Signed-OUT counterpart, added 6 Aug 2026. Deliberately NOT tinted like
   __portal: this is a door, not a destination. It stays a plain nav link so it
   reads as "I already have an account" and is ignored by everyone who does not
   — which is almost everyone. The previous build showed no link at all when
   signed out, on the argument that absence was honest. It was half right: it
   also made the door unfindable on a phone, where the only other one was in
   the footer. */
.glow-nav__signin { color: var(--glow-muted); }
.glow-nav__signin:hover { color: var(--glow-burgundy); }

/* Footer social icons — a quiet row, same ink as the list links. The list's
   underline hover doesn't suit an icon, so these brighten instead. */
.glow-footer__socials {
  display: inline-flex;
  align-items: center;
  gap: 1.1rem;
  margin-top: var(--glow-s2);
}
.glow-footer__socials a {
  display: inline-flex;
  color: rgba(250, 247, 242, 0.75);
  border-bottom: 0;
  transition: color var(--glow-dur) var(--glow-ease);
}
.glow-footer__socials a:hover,
.glow-footer__socials a:focus-visible {
  color: var(--glow-cream);
  border-bottom: 0;
}
.glow-footer__socials svg { display: block; }
/* ==========================================================================
   Glow Cosmetique — shared interior-page layer
   Loaded via template_css on every non-home page. Strictly on the tokens.

   ⚠ The line that used to sit here — "_home.css is NOT loaded on interior
   pages" — was true under the old template_css split and false since main.css
   began bundling the system. Everything in css/glow/ now loads on every page,
   in the order main.css includes it. That stale sentence is why steps and FAQs
   were duplicated here for weeks after the duplication stopped being needed.
   ========================================================================== */

/* ---- Breadcrumb ---------------------------------------------------------
   Tiny orientation line for treatment pages: Treatments → name.
   Sits inside .glow-pagehead, above the tag. One level deep, no schema
   theatre — it's a pointer, not a nav system. */

.glow-crumb {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.6rem;
  font-size: var(--glow-fine);
  letter-spacing: 0.03em;
  color: var(--glow-muted);
  margin: 0 0 var(--glow-s4);
}

.glow-crumb a {
  color: var(--glow-muted);
  border-bottom: 1px solid transparent;
  transition: color var(--glow-dur) var(--glow-ease),
              border-color var(--glow-dur) var(--glow-ease);
}

.glow-crumb a:hover,
.glow-crumb a:focus-visible {
  color: var(--glow-ink);
  border-color: var(--glow-border);
}

.glow-crumb__sep { color: var(--glow-border); user-select: none; }

/* ---- Page header --------------------------------------------------------
   Quieter than the home hero: no image, one column, generous top padding.
   Grammar: [.glow-crumb] → .glow-tag → h1.glow-pagehead__title (one .glow-em
   phrase) → p.glow-lead (the 40–60 word answer block) → [__actions] → [__meta].
   The title runs a step below the home h1 so interior pages read as rooms,
   not entrances. */

.glow-pagehead {
  padding-block: clamp(3.5rem, 8vw, 6.5rem) clamp(2.5rem, 6vw, 4.5rem);
}

.glow-pagehead__title {
  font-size: clamp(2.5rem, 5.4vw, 4.25rem);
  max-width: 18ch;
  margin-bottom: var(--glow-s4);
}

.glow-pagehead__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--glow-s3) var(--glow-s4);
  margin-top: 2.25rem;
}

.glow-pagehead__meta {
  margin-top: var(--glow-s5);
  padding-top: var(--glow-s3);
  border-top: 1px solid var(--glow-border);
  max-width: 34ch;
  font-size: var(--glow-fine);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--glow-muted);
}

/* ---- Prose --------------------------------------------------------------
   Long-form content column. Headings keep the display face but drop below
   the section scale — inside a 62ch measure the full h2 shouts. Lists get
   the burgundy dash; links are quiet and underline on hover only. */

.glow-prose { max-width: var(--glow-measure); }

.glow-prose h2 {
  font-size: clamp(1.75rem, 2.6vw, 2.25rem);
  line-height: 1.15;
  margin: var(--glow-s6) 0 var(--glow-s3);
  max-width: 26ch;
}

.glow-prose h3 {
  margin: var(--glow-s5) 0 var(--glow-s2);
  max-width: 30ch;
}

.glow-prose h2:first-child,
.glow-prose h3:first-child { margin-top: 0; }

.glow-prose p { margin: 0 0 var(--glow-s3); }

.glow-prose ul,
.glow-prose ol {
  margin: 0 0 var(--glow-s3);
  padding: 0;
  list-style: none;
  max-width: var(--glow-measure);
}

.glow-prose li {
  position: relative;
  padding-left: 1.6rem;
  margin-bottom: 0.6rem;
}

.glow-prose ul li::before {
  content: '\2014'; /* em dash */
  position: absolute;
  left: 0;
  color: var(--glow-burgundy);
}

.glow-prose ol { counter-reset: glow-prose-ol; }

.glow-prose ol li::before {
  counter-increment: glow-prose-ol;
  content: counter(glow-prose-ol);
  position: absolute;
  left: 0;
  font-family: var(--glow-font-display);
  color: var(--glow-burgundy);
}

.glow-prose a {
  color: var(--glow-burgundy);
  border-bottom: 1px solid transparent;
  transition: border-color var(--glow-dur) var(--glow-ease);
}

.glow-prose a:hover,
.glow-prose a:focus-visible { border-bottom-color: var(--glow-burgundy); }

.glow-prose strong { font-weight: 500; color: var(--glow-ink); }

.glow-prose blockquote {
  margin: var(--glow-s4) 0;
  padding-left: var(--glow-s3);
  border-left: 1px solid var(--glow-burgundy);
  font-family: var(--glow-font-display);
  font-size: var(--glow-t3);
  line-height: 1.4;
  color: var(--glow-ink);
}

.glow-prose blockquote p { margin: 0; }

/* ---- Prose rules and tables ---------------------------------------------
   Added 4 Aug 2026 when the first nine articles went into HubSpot. The
   markdown drafts use `---` and pipe tables, both of which were rendering
   as browser defaults on top of everything else being deliberate — and the
   table was pushing past the measure rather than scrolling inside it.

   Written to the same rules as the portal's analyte rows: hairline
   separators, no vertical lines, no zebra striping. A table on a clinical
   page is read across a row, and anything that draws the eye down a column
   is fighting that. */

.glow-prose hr {
  border: 0;
  border-top: 1px solid var(--glow-border);
  margin: var(--glow-s5) 0;
  max-width: var(--glow-measure);
}

/* The scroll container, not the table. A wide table has to scroll inside
   itself — the page body must never scroll sideways. */
.glow-tablewrap {
  max-width: var(--glow-measure);
  overflow-x: auto;
  margin: 0 0 var(--glow-s4);
  -webkit-overflow-scrolling: touch;
}

/* Reading measure is right for prose and wrong for a wide comparison table —
   /comparisons runs four columns across eleven treatments and the cap turns it
   into a column of fragments. Opt-in, so no existing table moves. */
.glow-tablewrap--wide { max-width: none; }

.glow-prose table {
  width: 100%;
  border-collapse: collapse;
  /* Element-level reset. theme-overrides.css no longer loads (B-06), but this
     stays as the floor so a bare <table> is never at the mercy of whatever
     stylesheet a future page pulls in. Border on the TABLE, not just cells —
     missing that is what put a box back on 30 Aug. */
  background-color: transparent;
  border: 0;
  font-size: var(--glow-small);
  line-height: 1.5;
  /* Below this the cells wrap to one word a line and the row stops being
     readable, so it scrolls instead. */
  min-width: 30rem;
}

.glow-prose thead th {
  text-align: left;
  font-size: var(--glow-meta);
  font-weight: 600;
  letter-spacing: var(--glow-eyebrow-track);
  text-transform: uppercase;
  color: var(--glow-muted);
  background-color: transparent;
  padding: 0 var(--glow-s2) 0.6rem 0;
  border: 0;
  border-bottom: 1px solid var(--glow-border);
}

/* An empty header cell is normal here — several tables are two-column
   comparisons where the left column needs no label. Collapse it rather than
   leaving a stub of border. */
.glow-prose thead th:empty { padding: 0; }

.glow-prose tbody td {
  padding: 0.7rem var(--glow-s2) 0.7rem 0;
  background-color: transparent;
  border: 0;
  border-bottom: 1px solid var(--glow-border);
  vertical-align: top;
}

.glow-prose tbody tr:last-child td { border-bottom: 0; }
.glow-prose table th:last-child,
.glow-prose table td:last-child { padding-right: 0; }

/* First column carries the label in most of these tables. */
.glow-prose tbody td:first-child { color: var(--glow-ink); }

/* ---- Spec grid ----------------------------------------------------------
   Treatment facts on a blush field: duration, price, frequency, suits,
   doesn't suit. Markup is a <dl>:

     <dl class="glow-spec">
       <div class="glow-spec__item">
         <dt class="glow-spec__term">Price</dt>
         <dd class="glow-spec__desc">$250</dd>
       </div>
       <div class="glow-spec__item glow-spec__item--wide">
         <dt class="glow-spec__term">Suits</dt>
         <dd class="glow-spec__note">Sentence-length value.</dd>
       </div>
     </dl>

   __desc is for short facts (serif, large); __note for sentence values.
   --wide spans the full row for the sentence-length items. */

.glow-spec {
  display: grid;
  gap: var(--glow-s4);
  margin: 0;
  background: var(--glow-blush);
  border-radius: var(--glow-radius);
  padding: var(--glow-s4);
}

@media (min-width: 640px) {
  .glow-spec { grid-template-columns: repeat(2, 1fr); padding: var(--glow-s5); }
}

@media (min-width: 1000px) {
  .glow-spec { grid-template-columns: repeat(4, 1fr); }
}

.glow-spec__item { min-width: 0; }

.glow-spec__item--wide { grid-column: 1 / -1; }

.glow-spec__term {
  font-size: var(--glow-eyebrow-size);
  font-weight: var(--glow-eyebrow-weight);
  letter-spacing: var(--glow-eyebrow-track);
  text-transform: uppercase;
  color: var(--glow-muted);
  margin: 0 0 0.4rem;
}

.glow-spec__desc {
  margin: 0;
  font-family: var(--glow-font-display);
  font-size: var(--glow-t3);
  line-height: 1.3;
  color: var(--glow-ink);
}

/* ---- Spec, PROSE variant ------------------------------------------------
   Added 6 Aug 2026, after seeing /ingredients and /concerns rendered.

   THE DIAGNOSIS IS A MISUSE, NOT A BUG. .glow-spec was built for SHORT values
   — its own doc comment shows `<dt>Price</dt><dd>$250</dd>`. The __desc is
   styled as a display-face VALUE: serif, 1.375rem, line-height 1.3. That is
   right for "$250" and wrong for a forty-word sentence, which is what the four
   new hub pages put in it.

   What it produced: eight items across four columns, one running eight lines
   deep and setting the height of the whole row, then a full-width final item
   whose measure ran past 140 characters.

   So: two columns rather than four, the display face kept but stepped down,
   and line-height opened up. Same component, same voice, built for sentences.

   Two columns is also why the counts work — 4, 6 and 8 items all give whole
   rows, where four columns left a 4/3/1 orphan on every page using it. */
.glow-spec--prose { gap: var(--glow-s5) var(--glow-s6); }

@media (min-width: 1000px) {
  .glow-spec--prose { grid-template-columns: repeat(2, 1fr); }
}

.glow-spec--prose .glow-spec__desc {
  font-size: var(--glow-t5);
  line-height: 1.45;
  max-width: 46ch;
}

/* A full-width item in a TWO-column grid reads as a closing summary rather
   than an orphan — but the container is ~90ch and prose is unreadable at that
   width, so the measure is capped rather than the item. */
.glow-spec--prose .glow-spec__item--wide .glow-spec__desc { max-width: 74ch; }

.glow-spec__note {
  margin: 0;
  font-size: var(--glow-small);
  line-height: 1.6;
  color: var(--glow-ink-soft);
  max-width: 62ch;
}


/* ---- Comparison table ---------------------------------------------------
   /comparisons. Added 6 Aug 2026 to override the HubSpot boilerplate.

   WHY THIS EXISTS RATHER THAN JUST USING `.glow-prose table`. The theme ships
   `css/theme-overrides.css`, which styles every bare <table> with HubSpot's
   default: a filled dark header bar and a border on every cell. That is what
   rendered — a heavy grid with centred bold row headers, in a design system
   whose every other table is hairline-separated.

   The centred row headers were a second, quieter defect: `.glow-prose thead th`
   sets text-align left, and `tbody th` had no rule at all, so it fell through
   to the browser default of centre + bold. Every treatment name was centred in
   a left-aligned table and nobody would have called it a bug — it just looked
   slightly wrong.

   Grammar follows .glow-pricetable: hairline rows, blush hover, display face
   on the number, and nothing vertical anywhere. */
.glow-comparetable {
  width: 100%;
  border-collapse: collapse;
  margin: 0;
  min-width: 44rem;
  background: none;
  border: 0;
}

.glow-comparetable th,
.glow-comparetable td {
  background: none;
  border: 0;
  border-bottom: 1px solid var(--glow-border);
}

.glow-comparetable thead th {
  font-family: var(--glow-font-body);
  font-size: var(--glow-meta);
  font-weight: 600;
  letter-spacing: var(--glow-eyebrow-track);
  text-transform: uppercase;
  color: var(--glow-muted);
  text-align: left;
  padding: 0 var(--glow-s3) 0.7rem 0;
  white-space: nowrap;
}

.glow-comparetable tbody th,
.glow-comparetable tbody td {
  padding: var(--glow-s3) var(--glow-s3) var(--glow-s3) 0;
  vertical-align: top;
  transition: background var(--glow-dur) var(--glow-ease);
}

/* The fix for the centred names. Also drops the browser's bold — emphasis is
   carried by the link colour, and two emphases on one cell is one too many. */
.glow-comparetable tbody th {
  text-align: left;
  font-weight: 500;
  color: var(--glow-ink);
  width: 30%;
}

.glow-comparetable tbody tr:hover th,
.glow-comparetable tbody tr:hover td { background: var(--glow-blush); }

/* Numbers in the display face, as they are on /pricing. Nowrap because
   "By consultation" breaking mid-phrase was half the raggedness. */
.glow-comparetable__price {
  font-family: var(--glow-font-display);
  font-size: var(--glow-t4);
  color: var(--glow-ink);
  white-space: nowrap;
  width: 14%;
}

/* "Assessment first" wrapped onto two lines on seven of eleven rows, which is
   what made the column look broken. It is a label, so it never wraps. */
.glow-comparetable__gate {
  font-size: var(--glow-small);
  color: var(--glow-ink-soft);
  white-space: nowrap;
  width: 18%;
}

.glow-comparetable__for {
  font-size: var(--glow-small);
  line-height: 1.55;
  color: var(--glow-muted);
  max-width: 42ch;
}

/* MOBILE: STACK, DO NOT SCROLL.
   The tablewrap makes a wide table scroll inside itself, which is correct for
   a page body and wrong for the primary content of a page. On a 390px phone
   four columns means the reader has to discover a sideways scroll to reach
   "Broadly for" — and most never do. Glow's clients are on phones.

   So below 720px the table stops being a table and becomes one card per
   treatment: name, then price and booking on a line, then the description.
   The <th>/<td> semantics and the header row are untouched, so a screen
   reader and a crawler still see a table — only the visual layout changes. */
@media (max-width: 720px) {
  .glow-comparetable { min-width: 0; display: block; }
  .glow-comparetable thead { display: none; }
  .glow-comparetable tbody,
  .glow-comparetable tbody tr { display: block; }

  .glow-comparetable tbody tr {
    padding: var(--glow-s3) 0;
    border-bottom: 1px solid var(--glow-border);
  }
  .glow-comparetable tbody tr:hover th,
  .glow-comparetable tbody tr:hover td { background: none; }

  .glow-comparetable tbody th,
  .glow-comparetable tbody td {
    display: block;
    width: auto;
    border: 0;
    padding: 0;
  }

  .glow-comparetable tbody th {
    font-size: var(--glow-body);
    margin-bottom: 0.35rem;
  }

  /* Price and gate share a line — they are the two facts being compared, and
     stacking them wastes the height that stacking was meant to buy.

     ⚠ SELECTOR MUST CARRY `td`. Measured: the first version used the bare
     class and lost. `.glow-comparetable tbody td` is one class plus two
     elements (0,1,2); `.glow-comparetable__price` is one class (0,1,0). The
     block rule above won on specificity and the two sat on separate lines —
     rendering perfectly, just not as designed. Nothing errors when a CSS rule
     silently loses, which is the same shape as every other bug on this build. */
  .glow-comparetable tbody td.glow-comparetable__price {
    display: inline-block;
    font-size: var(--glow-t5);
    margin-right: var(--glow-s2);
  }
  .glow-comparetable tbody td.glow-comparetable__gate {
    display: inline-block;
    color: var(--glow-muted);
  }
  .glow-comparetable__gate::before { content: "· "; }

  .glow-comparetable__for {
    max-width: none;
    margin-top: 0.4rem;
  }
}

/* ---- Price table --------------------------------------------------------
   The full published list. Group heading rows carry the display face;
   item rows are hairline-separated with a blush hover; prices sit right.

     <table class="glow-pricetable">
       <tbody>
         <tr class="glow-pricetable__group"><th colspan="2" scope="colgroup">Facials</th></tr>
         <tr class="glow-pricetable__row">
           <td class="glow-pricetable__item">Glass Skin Facial
             <span class="glow-pricetable__detail">90 minutes</span></td>
           <td class="glow-pricetable__price">$250</td>
         </tr>
       </tbody>
     </table> */

/* ⚠ WIDTH ADDED 20 Aug 2026, and it is the reason /pricing read as unbalanced
   against every other page. The table was width:100% inside a 1280px
   container while all prose on the site sits at --glow-measure (62ch, about
   650px). So a service name sat on the far left and its price ~1280px away,
   with nothing between them — the eye cannot carry a row across that, and the
   page looked like a different site.

   70ch is measure plus a price column: wide enough for a 52ch detail line
   (--glow-pricetable__detail already caps at that), narrow enough that name
   and price stay in one glance. Left-aligned rather than centred so it keeps
   the same left edge as the prose above it. */
.glow-pricetable {
  width: 100%;
  max-width: 70ch;
  border-collapse: collapse;
  margin: 0;
  /* Same element-level floor as .glow-prose table — this one sits OUTSIDE
     .glow-prose, so it had no defence at all and was rendering the full grid. */
  background-color: transparent;
  border: 0;
}

.glow-pricetable th,
.glow-pricetable td {
  background-color: transparent;
  border: 0;
}

.glow-pricetable__group th {
  font-family: var(--glow-font-display);
  font-size: var(--glow-t3);
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: -0.005em;
  color: var(--glow-ink);
  text-align: left;
  padding: var(--glow-s6) var(--glow-s3) var(--glow-s2);
  border-bottom: 1px solid var(--glow-border);
}

/* First group in the table sits flush with whatever precedes it. */
.glow-pricetable tbody:first-child > .glow-pricetable__group:first-child th {
  padding-top: 0;
}

.glow-pricetable__row td {
  padding: var(--glow-s3);
  border-bottom: 1px solid var(--glow-border);
  vertical-align: top;
  transition: background var(--glow-dur) var(--glow-ease);
}

.glow-pricetable__row:hover td { background: var(--glow-blush); }

.glow-pricetable__item { color: var(--glow-ink); }

.glow-pricetable__detail {
  display: block;
  font-size: var(--glow-small);
  color: var(--glow-muted);
  margin-top: 0.2rem;
  max-width: 52ch;
}

.glow-pricetable__price {
  font-family: var(--glow-font-display);
  font-size: var(--glow-t4);
  color: var(--glow-ink);
  text-align: right;
  white-space: nowrap;
}

/* ---- CTA band -----------------------------------------------------------
   End-of-page close. Light variant sits on blush; --deep takes the deep
   field for pages that need the candour weight at the end. One button,
   stated once — heading + .glow-button, optional short __desc. */

.glow-cta-band {
  display: grid;
  gap: var(--glow-s4);
  align-items: center;
  justify-items: start;
  background: var(--glow-blush);
  border-radius: var(--glow-radius);
  padding: clamp(2.5rem, 6vw, 4.5rem) clamp(1.5rem, 4vw, 4rem);
}

@media (min-width: 860px) {
  .glow-cta-band { grid-template-columns: 1fr auto; gap: var(--glow-s5); }
}

.glow-cta-band__title {
  font-size: clamp(1.75rem, 3.2vw, 2.5rem);
  line-height: 1.12;
  max-width: 22ch;
  margin: 0 0 var(--glow-s2);
}

.glow-cta-band__title:last-child { margin-bottom: 0; }

.glow-cta-band__desc {
  font-size: var(--glow-body);
  color: var(--glow-ink-soft);
  max-width: 48ch;
  margin: 0;
}

.glow-cta-band--deep {
  background: var(--glow-deep);
  color: rgba(250, 247, 242, 0.72);
}

.glow-cta-band--deep .glow-cta-band__title { color: var(--glow-cream); }

.glow-cta-band--deep .glow-cta-band__desc { color: rgba(250, 247, 242, 0.62); }

/* On the deep field the button's hover state would sink into the band —
   invert it instead. */
.glow-cta-band--deep .glow-button:hover,
.glow-cta-band--deep .glow-button:focus-visible {
  background: var(--glow-cream);
  border-color: var(--glow-cream);
  color: var(--glow-ink);
}

/* ==========================================================================
   Steps and FAQs — DELETED FROM HERE 30 August 2026. They live in _home.css.

   They were duplicated byte-for-byte in both files under a "keep in sync"
   comment, on the premise stated at the top of this file: "only one of the two
   files is ever loaded on a page". That stopped being true when main.css began
   bundling the whole system — _page.css and _home.css now BOTH load on every
   page, and _home.css loads second, so its copy was already the one winning.

   Two rules that must agree have to share a definition. A "keep in sync"
   comment is not a constraint, it is a bug that has not happened yet — the same
   fault that copied the button geometry into four files and let the card header
   drift from the card body. B-08 §9.
   ========================================================================== */

/* ==========================================================================
   ARTICLE — the three blog streams
   templates/blog-post.html · templates/blog-index.html
   ========================================================================== */

/* Byline. Small, but it is the thing that separates this from the anonymous
   content the rest of the industry publishes, so it is ink rather than muted —
   the author's name is a claim, not metadata. */
.glow-article__byline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
  font-size: var(--glow-small);
  color: var(--glow-muted);
  margin: var(--glow-s3) 0 0;
  max-width: none;
}

.glow-article__author { font-weight: 500; color: var(--glow-ink); }

.glow-article__body { max-width: 34rem; margin-inline: auto; }

/* ── THE ARTICLE COLUMN IS CENTRED. Fixed 5 Aug 2026, reported by Hemi.
   ─────────────────────────────────────────────────────────────────────────
   Measured on /notes/faces-change-structurally-not-just-superficially at
   1440: the article body was 544px pinned to the LEFT of a 1280px container,
   leaving 760px of empty page beside every paragraph.

   THE CAUSE IS INHERITANCE, NOT AN OVERSIGHT IN THIS FILE. Every other
   template opens with the same measure-width, left-aligned .glow-pagehead and
   then follows it with a full-width .glow-split — verified on /approach, where
   the splits run the whole 1168px. The header reads as left-aligned because
   something wide sits under it.

   Long-form is the one page type where the measure-width column IS the whole
   page. It took the header pattern and never got the wide content that
   justifies it, so the layout the rest of the site uses correctly becomes a
   column of text against a void.

   Only the header and the body narrow. The related-posts index and the CTA
   band below them keep the full container — they are built to span it, and a
   full-width band is the correct punctuation at the end of a narrow read.

   NOT a fix for the table, which was the other half of the report. The table
   is inside .glow-tablewrap with overflow-x:auto and it behaves: at 1440 it
   fits at 544px with no clipping. Below ~700px it scrolls, which is correct —
   macOS hides the scrollbar until it is touched, so a screenshot of a scrolling
   table is indistinguishable from a clipped one. */
.glow-article > header > .glow-container,
.glow-article > .glow-section--tight > .glow-container {
  max-width: calc(36rem + var(--glow-gutter) * 2);
}

/* Every H2 in a post is an answer-block heading (SEO-AEO-GEO §3) and gets an id
   so it is individually linkable. The anchor is invisible until the heading is
   hovered or focused — present for anyone who wants to quote a specific answer,
   absent for everyone reading. */
.glow-article__body h2 { position: relative; scroll-margin-top: var(--glow-s4); }

.glow-article__body h2 .glow-anchor {
  position: absolute;
  left: -1.25rem;
  opacity: 0;
  color: var(--glow-muted);
  text-decoration: none;
  transition: opacity var(--glow-dur) var(--glow-ease);
}

.glow-article__body h2:hover .glow-anchor,
.glow-article__body h2 .glow-anchor:focus-visible { opacity: 1; }

@media (max-width: 720px) { .glow-article__body h2 .glow-anchor { display: none; } }

.glow-article__tags {
  margin: var(--glow-s5) 0 0;
  font-size: var(--glow-small);
  color: var(--glow-muted);
  max-width: none;
}

.glow-article__tags a { color: var(--glow-muted); border-bottom: 1px solid transparent; }
.glow-article__tags a:hover { color: var(--glow-ink); border-color: var(--glow-border); }

/* The standing disclosure. Quiet, but on every article — the alternative is a
   reader taking a blog post as personal advice. */
.glow-article__note {
  margin: var(--glow-s4) 0 0;
  padding-top: var(--glow-s3);
  border-top: 1px solid var(--glow-border);
  font-size: var(--glow-small);
  color: var(--glow-muted);
  max-width: 34rem;
}

.glow-article__pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--glow-s3);
  margin-top: var(--glow-s5);
  padding-top: var(--glow-s3);
  border-top: 1px solid var(--glow-border);
}

.glow-article__pager-pos {
  font-size: var(--glow-small);
  color: var(--glow-muted);
}

/* ---- IMAGE SLOTS — staging only ------------------------------------------
   A shot list rendered in place, for the 8 Aug content review. See
   partials/imgslot.html for the compliance lines and the off switch.

   Styled to read as SCAFFOLDING, not as a design element: dashed, no fill,
   muted. Nobody should mistake one for a finished treatment, and nobody
   should be tempted to ship it. */
.glow-imgslot {
  margin: 0;
  border: 1px dashed var(--glow-border);
  border-left: 3px solid var(--glow-border);
  border-radius: var(--glow-radius);
  background: repeating-linear-gradient(
    135deg, transparent, transparent 9px,
    rgba(122, 106, 98, 0.045) 9px, rgba(122, 106, 98, 0.045) 18px);
  display: grid;
  place-items: center;
  width: 100%;
  overflow: hidden;
}

.glow-imgslot__in {
  display: flex; flex-direction: column; align-items: center; gap: 0.35rem;
  text-align: center; padding: var(--glow-s3); max-width: 46ch;
}

.glow-imgslot__pri {
  font-size: var(--glow-eyebrow-size);
  font-weight: var(--glow-eyebrow-weight);
  letter-spacing: var(--glow-eyebrow-track);
  text-transform: uppercase;
  color: var(--glow-muted);
}
.glow-imgslot__label { font-size: var(--glow-t5); line-height: 1.35; color: var(--glow-ink); }
.glow-imgslot__ratio {
  font-size: var(--glow-meta); font-variant-numeric: tabular-nums;
  color: var(--glow-muted); opacity: 0.8;
}
.glow-imgslot__note {
  font-size: var(--glow-small); line-height: 1.5; color: var(--glow-muted);
  margin-top: 0.35rem; max-width: 40ch;
}

/* Priority reads on the left edge only — enough to sort by, not enough to
   compete with the page. Amber is the site's one attention colour and it is
   reserved for "you cannot open without this". */
.glow-imgslot--must  { border-left-color: var(--glow-flag); }
.glow-imgslot--must  .glow-imgslot__pri { color: var(--glow-flag); }
.glow-imgslot--want  { border-left-color: var(--glow-burgundy); }
.glow-imgslot--maybe { border-left-color: var(--glow-border); opacity: 0.75; }

.glow-imgslot-row {
  display: grid; gap: var(--glow-s2);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}

@media (max-width: 620px) {
  .glow-imgslot__label { font-size: var(--glow-small); }
  .glow-imgslot__note  { display: none; }   /* the brief is for the desktop read */
}

/* ---- THE HOLDING PAGE ----------------------------------------------------
   One page, standing in for a whole site. See templates/holding.html for why
   it exists and what it deliberately omits.

   Sized generously and centred on its own — it is the only thing on the
   domain, so it does not have to compete with anything, and a cramped holding
   page reads as a site that has broken rather than one being rebuilt. */
.glow-hold { padding-block: clamp(3rem, 10vw, 7rem); }
.glow-hold .glow-container { max-width: 68ch; }

.glow-hold__title {
  font-family: var(--glow-font-display);
  font-size: clamp(2.25rem, 6vw, 3.5rem);
  line-height: 1.08;
  letter-spacing: -0.015em;
  color: var(--glow-ink);
  margin: var(--glow-s2) 0 var(--glow-s3);
}

.glow-hold__actions {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--glow-s3); margin-top: var(--glow-s4);
}

.glow-hold__grid {
  display: grid; gap: var(--glow-s3);
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  margin-top: var(--glow-s6);
  padding-top: var(--glow-s4);
  border-top: 1px solid var(--glow-border);
}

.glow-hold__lines {
  font-size: var(--glow-body); line-height: 1.55;
  color: var(--glow-ink); margin: var(--glow-s2) 0 0;
}
.glow-hold__note {
  font-size: var(--glow-small); line-height: 1.55;
  color: var(--glow-muted); margin: var(--glow-s2) 0 0;
}

.glow-hold__foot {
  font-size: var(--glow-small); line-height: 1.65; color: var(--glow-muted);
  margin: var(--glow-s5) 0 0; padding-top: var(--glow-s3);
  border-top: 1px solid var(--glow-border); max-width: 60ch;
}

/* ── ENQUIRY FORM ───────────────────────────────────────────────────────────
   Added 19 Aug 2026. /contact carried its form as raw <input> elements with
   an inline style="" on every field — no classes, nothing from the design
   system — and its own comment admitted it: "exists for build and preview
   only". It had been live long enough that Hemi asked why the page looked
   unlike the rest of the site. It looked unlike the site because it was not
   built from it.

   ⚠ THE INLINE-STYLE TRAP, worth naming: inline styles cannot be overridden
   by a stylesheet, so a page carrying them silently opts out of every future
   design change. It does not break, it does not warn, and it drifts further
   from the site every time the site improves.

   Label treatment matches .glow-q__label in _intake.css deliberately — a
   field is a field, and the client meets the same shape here that she meets
   on the health declaration. Labels are always visible; the intake rule
   against placeholder-only fields is not an intake rule, it is a Glow rule.
   ------------------------------------------------------------------------- */

.glow-fields {
  max-width: var(--glow-measure);
  margin-top: var(--glow-s5);
}

.glow-field + .glow-field { margin-top: var(--glow-s3); }

.glow-field__label {
  display: block;
  font-family: var(--glow-font-body);
  font-size: var(--glow-pt-row);
  font-weight: 500;
  line-height: 1.45;
  color: var(--glow-ink);
  margin-bottom: 0.6rem;
}

/* The optional marker sits inside the label rather than in the field, so a
   screen reader hears it as part of the question and it never becomes a
   placeholder that vanishes on focus. */
.glow-field__opt {
  font-weight: 400;
  color: var(--glow-muted);
}

.glow-field__input {
  display: block;
  width: 100%;
  padding: 0.85rem 1rem;
  font-family: var(--glow-font-body);
  font-size: var(--glow-small);
  line-height: 1.5;
  color: var(--glow-ink);
  background-color: var(--glow-white);
  border: 1px solid var(--glow-surface-border);
  border-radius: var(--glow-radius-ui);
  transition: border-color var(--glow-dur) var(--glow-ease);
  -webkit-appearance: none;
  appearance: none;
}

.glow-field__input::placeholder { color: var(--glow-muted); }

.glow-field__input:hover { border-color: var(--glow-ink-soft); }

/* Matches the global :focus-visible ring in _base.css rather than inventing a
   second focus language. The border darkens as well so the field still reads
   as active for anyone who has outlines suppressed. */
.glow-field__input:focus-visible {
  border-color: var(--glow-burgundy);
  outline: 2px solid var(--glow-burgundy);
  outline-offset: 2px;
}

textarea.glow-field__input {
  resize: vertical;
  min-height: 8.5rem;
}

.glow-fields__actions { margin-top: var(--glow-s4); }

/* The line under the button — what happens after you send. It is a statement
   of fact, not reassurance, and it belongs with the action rather than in the
   prose above it. */
.glow-fields__after {
  margin-top: var(--glow-s2);
  font-size: var(--glow-small);
  line-height: 1.55;
  color: var(--glow-muted);
}

/* The phone number on /contact, set as display type and sized as a tap target.
   It was composed inline — "no new classes" was the note, and the cost of that
   was that the most prominent element on the page after the h1 could not be
   restyled with the site. A class is cheaper than the exception. */
.glow-telcta {
  display: inline-block;
  font-family: var(--glow-font-display);
  font-size: var(--glow-h2);
  line-height: 1.1;
  color: var(--glow-ink);
  text-decoration: none;
  padding: var(--glow-s1) 0;
  transition: color var(--glow-dur) var(--glow-ease);
}
.glow-telcta:hover { color: var(--glow-burgundy); }

/* ---- MODULE RHYTHM — the public site's missing rule ---------------------
   Added 30 Aug 2026, B-08 §6.

   ⚠ THIS BLOCK CHANGES NOTHING VISUALLY. Every value is the inline style it
   replaces, to the pixel — 89 .glow-spec, 48 .glow-index, 16 .glow-faqs and
   2 .glow-pricetable instances each carried `style="margin-top: var(--glow-s6)"`
   in the markup, and now inherit it from here instead.

   WHY IT MATTERS. The portal has had this rule since 5 Aug and its spacing has
   been stable ever since; the public site never got one, so 236 separate
   authors' decisions accumulated in 72 templates — including sixteen written as
   raw literals. scripts/portal-type-lint.sh's own header diagnoses this exact
   fault for the portal: "there was no class for a standalone link row, so nine
   authors each wrote the margin inline." The lesson was written down, fixed for
   the portal, and never applied to the site.

   The :first-child reset mirrors .glow-block's, which has held since 5 Aug. */
.glow-spec,
.glow-index,
.glow-faqs,
.glow-pricetable { margin-top: var(--glow-s6); }

.glow-spec:first-child,
.glow-index:first-child,
.glow-faqs:first-child,
.glow-pricetable:first-child { margin-top: 0; }
/* ==========================================================================
   Glow Cosmetique — home page
   ========================================================================== */

/* ---- Hero --------------------------------------------------------------
   Asymmetric. Statement left, image right bleeding to the viewport edge,
   with a tinted field offset behind it. Composition rather than decoration. */

.glow-hero {
  padding-block: clamp(3rem, 7vw, 6rem) clamp(4rem, 9vw, 7rem);
  overflow: hidden;
}

.glow-hero__grid { display: grid; gap: var(--glow-s6); }

@media (min-width: 980px) {
  .glow-hero__grid {
    grid-template-columns: minmax(0, 0.98fr) minmax(0, 1fr);
    gap: var(--glow-s7);
    align-items: center;
  }
}

.glow-hero__figure { position: relative; order: -1; }

@media (min-width: 980px) {
  .glow-hero__figure { order: 0; margin-right: calc(50% - 50vw); }
}

.glow-hero__figure::before {
  content: '';
  position: absolute;
  left: -2rem; bottom: -2rem;
  width: 55%; height: 70%;
  background: var(--glow-blush);
  border-radius: var(--glow-radius);
  z-index: 0;
}

.glow-hero__figure img {
  position: relative; z-index: 1;
  width: 100%;
  height: clamp(23rem, 58vh, 38rem);
  object-fit: cover;
  object-position: 32% 18%;
  border-radius: var(--glow-radius) 0 0 var(--glow-radius);
}

@media (max-width: 979px) {
  .glow-hero__figure img { border-radius: var(--glow-radius); }
}

.glow-hero__title { max-width: 19ch; margin-bottom: var(--glow-s4); }

.glow-hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--glow-s3) var(--glow-s4);
  margin-top: 2.25rem;
}

.glow-hero__meta {
  margin-top: var(--glow-s5);
  padding-top: var(--glow-s3);
  border-top: 1px solid var(--glow-border);
  max-width: 34ch;
  font-size: var(--glow-fine);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--glow-muted);
}

/* ---- Split header — MOVED to _base.css, 9 Aug 2026 ----------------------
   It was declared identically here and in the other file with a "keep in
   sync" comment. One copy now, next to .glow-container. */

/* ---- Problem cards ------------------------------------------------------
   The empathy beat. The client's own words as the headings. */

.glow-problems {
  display: grid;
  gap: 0.5rem;
  margin-top: var(--glow-s6);
}

@media (min-width: 860px) {
  .glow-problems { grid-template-columns: repeat(3, 1fr); }
}

.glow-problem {
  background: var(--glow-blush);
  border-radius: var(--glow-radius);
  padding: var(--glow-s5) var(--glow-s4);
}

.glow-problem__title {
  font-family: var(--glow-font-display);
  font-size: var(--glow-t3);
  line-height: 1.25;
  color: var(--glow-ink);
  margin: 0 0 var(--glow-s2);
}

.glow-problem__desc {
  font-size: var(--glow-small);
  color: var(--glow-muted);
  margin: 0;
}

/* ---- FAQ preview --------------------------------------------------------
   Native details/summary. Answers follow the 40–60 word answer-block
   pattern so they read the same here as in a snippet. */

.glow-faqs {
  margin-top: var(--glow-s6);
  border-top: 1px solid var(--glow-border);
}

.glow-faq { border-bottom: 1px solid var(--glow-border); }

.glow-faq__q {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--glow-s4);
  cursor: pointer;
  list-style: none;
  font-family: var(--glow-font-display);
  font-size: var(--glow-t3);
  line-height: 1.3;
  color: var(--glow-ink);
  padding-block: var(--glow-s4);
}

.glow-faq__q::-webkit-details-marker { display: none; }

.glow-faq__q::after {
  content: '+';
  flex: none;
  font-family: var(--glow-font-body);
  font-size: var(--glow-t4);
  color: var(--glow-burgundy);
  transition: transform var(--glow-dur) var(--glow-ease);
}

.glow-faq[open] .glow-faq__q::after { transform: rotate(45deg); }

.glow-faq__a {
  font-size: var(--glow-small);
  color: var(--glow-ink-soft);
  max-width: 62ch;
  margin: 0;
  padding: 0 0 var(--glow-s4);
}

/* ---- Steps -------------------------------------------------------------
   Soft tinted rows. A sequence, not a funnel — no progress state, no
   completion, nothing to keep up with. */

.glow-steps {
  display: grid;
  gap: 0.5rem;
  margin-top: var(--glow-s6);
}

.glow-step {
  display: grid;
  grid-template-columns: 2.5rem 1fr;
  gap: var(--glow-s2) var(--glow-s3);
  align-items: baseline;
  background: var(--glow-blush);
  border-radius: var(--glow-radius);
  padding: var(--glow-s4);
}

@media (min-width: 780px) {
  .glow-step {
    grid-template-columns: 3.5rem 13rem 1fr;
    gap: var(--glow-s4);
    padding: var(--glow-s4) var(--glow-s5);
    align-items: center;
  }
}

.glow-step__num {
  font-family: var(--glow-font-display);
  font-size: var(--glow-body);
  color: var(--glow-burgundy);
}

/* Timed variant — /consultations/new-guest puts the minute range in the number
   slot ("40–50") instead of an ordinal ("02"), so the column needs the room.
   Added 31 Aug 2026 with Taliha's signed consultation structure. */
.glow-steps--timed .glow-step { grid-template-columns: 4rem 1fr; }
.glow-steps--timed .glow-step__num { white-space: nowrap; }

@media (min-width: 780px) {
  .glow-steps--timed .glow-step { grid-template-columns: 5.5rem 12rem 1fr; }
}

.glow-step__name {
  font-family: var(--glow-font-display);
  font-size: var(--glow-t3);
  color: var(--glow-ink);
  line-height: 1.2;
}

.glow-step__desc {
  font-size: var(--glow-small);
  color: var(--glow-muted);
  margin: 0;
  max-width: 54ch;
}

@media (max-width: 779px) {
  .glow-step__desc { grid-column: 2; }
}

/* ---- Limits ------------------------------------------------------------
   Deep field. Doctrine: authority comes from limits, so this gets the
   strongest visual weight on the page. */

.glow-limits { background: var(--glow-deep); color: rgba(250, 247, 242, 0.72); }
.glow-limits h2 { color: var(--glow-cream); }

.glow-limits__grid { display: grid; gap: 0.5rem; margin-top: var(--glow-s6); }

@media (min-width: 780px) {
  .glow-limits__grid { grid-template-columns: repeat(2, 1fr); }
}

.glow-limit {
  background: rgba(250, 247, 242, 0.055);
  border-radius: var(--glow-radius);
  padding: var(--glow-s4);
}

@media (min-width: 780px) { .glow-limit { padding: var(--glow-s5); } }

.glow-limit__title {
  font-family: var(--glow-font-display);
  font-size: var(--glow-t3);
  color: var(--glow-cream);
  margin: 0 0 var(--glow-s2);
  line-height: 1.25;
}

.glow-limit__desc {
  font-size: var(--glow-small);
  color: rgba(250, 247, 242, 0.62);
  margin: 0;
  max-width: 44ch;
}

/* ---- Treatments --------------------------------------------------------- */

.glow-treatments { margin-top: var(--glow-s6); display: grid; gap: 0.5rem; }

.glow-treatment {
  display: flex; flex-wrap: wrap;
  justify-content: space-between; align-items: baseline;
  gap: var(--glow-s2);
  background: var(--glow-blush);
  border-radius: var(--glow-radius);
  padding: var(--glow-s3) var(--glow-s4);
  transition: background var(--glow-dur) var(--glow-ease);
}

@media (min-width: 780px) { .glow-treatment { padding: var(--glow-s3) var(--glow-s5); } }

.glow-treatment:hover { background: var(--glow-light); }

.glow-treatment__name {
  font-family: var(--glow-font-display);
  font-size: var(--glow-t3);
  color: var(--glow-ink);
}

.glow-treatment__meta {
  font-size: var(--glow-small);
  color: var(--glow-muted);
}

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

.glow-close__title { max-width: 18ch; margin-bottom: var(--glow-s4); }
/* ==========================================================================
   Glow Cosmetique — shop
   Product grid, filter chips, product detail. Strictly on the tokens:
   blush fields, hairlines, serif display, pill controls. No badges, no
   sale flashes, no urgency furniture — the shelf is quiet by design.
   ========================================================================== */

/* ---- Filter chips -------------------------------------------------------
   Anchor-based for v1: brand chips link to /shop/brands/<brand>, category
   chips jump to in-page anchors. No JS. */

.glow-shop-filter {
  display: grid;
  gap: var(--glow-s2);
  margin-top: var(--glow-s6);
  padding-top: var(--glow-s4);
  border-top: 1px solid var(--glow-border);
}

.glow-chip-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.glow-chip-row__label {
  flex: none;
  min-width: 5.5rem;
  font-size: var(--glow-eyebrow-size);
  font-weight: var(--glow-eyebrow-weight);
  letter-spacing: var(--glow-eyebrow-track);
  text-transform: uppercase;
  color: var(--glow-muted);
}

.glow-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  font-family: var(--glow-font-body);
  font-size: var(--glow-fine);
  font-weight: 500;
  color: var(--glow-ink-soft);
  background: transparent;
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius-sm);
  padding: 0.4rem 0.95rem;
  white-space: nowrap;
  transition: background var(--glow-dur) var(--glow-ease),
              border-color var(--glow-dur) var(--glow-ease),
              color var(--glow-dur) var(--glow-ease);
}

.glow-chip:hover,
.glow-chip:focus-visible {
  background: var(--glow-blush);
}

.glow-chip--active {
  color: var(--glow-cream);
  background: var(--glow-deep);
  border-color: var(--glow-deep);
}

.glow-chip--active:hover { background: var(--glow-deep); }

.glow-chip__count {
  font-size: var(--glow-tag);
  font-weight: 400;
  color: var(--glow-muted);
}

.glow-chip--active .glow-chip__count { color: rgba(250, 247, 242, 0.6); }

/* ---- Category sections -------------------------------------------------- */

.glow-shop-cat { scroll-margin-top: 7rem; }
.glow-shop-cat + .glow-shop-cat { margin-top: var(--glow-s7); }

.glow-shop-cat__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--glow-s3);
  border-top: 1px solid var(--glow-border);
  padding-top: var(--glow-s3);
  margin-bottom: var(--glow-s4);
}

.glow-shop-cat__count {
  font-size: var(--glow-small);
  color: var(--glow-muted);
}

/* ---- Product grid -------------------------------------------------------
   2-up mobile, 3-up tablet, 4-up desktop. Tight column gap echoes the
   tiled blush rows used elsewhere on the site. */

.glow-products {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--glow-s5) 0.5rem;
}

@media (min-width: 720px)  { .glow-products { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1080px) { .glow-products { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* Related products on the detail page cap at 4-up too; same grid. */
.glow-products--related { margin-top: var(--glow-s6); }

/* ---- Product card -------------------------------------------------------
   Image on a blush field. Multiply blend folds white studio backgrounds
   into the tint so shots read as cut-outs without re-editing. */

.glow-product {
  display: flex;
  flex-direction: column;
}

.glow-product__link { display: block; }

.glow-product__media {
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 5;
  background: var(--glow-blush);
  border-radius: var(--glow-radius);
  padding: 11%;
  overflow: hidden;
}

.glow-product__media img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  mix-blend-mode: multiply;
  transition: transform var(--glow-dur) var(--glow-ease);
}

.glow-product__link:hover .glow-product__media img,
.glow-product__link:focus-visible .glow-product__media img {
  transform: scale(1.04);
}

.glow-product__brand {
  display: block;
  margin-top: var(--glow-s3);
  font-size: var(--glow-eyebrow-size);
  font-weight: var(--glow-eyebrow-weight);
  letter-spacing: var(--glow-eyebrow-track);
  text-transform: uppercase;
  color: var(--glow-muted);
}

/* Card title: t4, one rank below t3. It was t3 until 31 Aug 2026 — at 22px
   in a four-up grid, a long compound name ("Clear as Day Fragrance Free
   SPF50+ Dreamscreen") wraps to three serif lines and the card top turns
   into a slab. Hemi called it: tight, messy-side, hard to read. A card is a
   list item, not a section — it takes the line-item rank. */
.glow-product__title {
  display: block;
  margin-top: 0.35rem;
  font-family: var(--glow-font-display);
  font-size: var(--glow-t4);
  line-height: 1.3;
  color: var(--glow-ink);
}

/* The size line had NO rule at all until 31 Aug 2026 — it inherited body
   ink at full size, flush under the title, and read as a second title.
   It is a spec, so it dresses like one: small, muted, a breath of space. */
.glow-product__size {
  display: block;
  margin-top: 0.3rem;
  font-size: var(--glow-small);
  color: var(--glow-muted);
}

.glow-product__oos {
  font-size: var(--glow-small);
  color: var(--glow-muted);
}

.glow-product__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--glow-s2);
  margin-top: auto;
  padding-top: var(--glow-s2);
}

.glow-product__price {
  font-size: var(--glow-small);
  color: var(--glow-ink);
}

/* ---- Add to cart --------------------------------------------------------
   .glow-add is the contract with the cart script (built separately): it
   binds to this class and reads the data-* attributes. Quiet outline pill
   on cards; --solid variant matches .glow-button on the detail page. */

.glow-add {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-family: var(--glow-font-body);
  font-size: var(--glow-fine);
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--glow-burgundy);
  background: transparent;
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius-ui);
  padding: 0.45rem 1rem;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--glow-dur) var(--glow-ease),
              border-color var(--glow-dur) var(--glow-ease),
              color var(--glow-dur) var(--glow-ease);
}

.glow-add:hover,
.glow-add:focus-visible {
  color: var(--glow-cream);
  background: var(--glow-burgundy);
  border-color: var(--glow-burgundy);
}

.glow-add--solid {
  color: var(--glow-cream);
  background: var(--glow-burgundy);
  border-color: var(--glow-burgundy);
  font-size: var(--glow-small);
  padding: 0.9rem 1.75rem;
}

.glow-add--solid:hover,
.glow-add--solid:focus-visible {
  background: var(--glow-deep);
  border-color: var(--glow-deep);
}

/* Brief confirm state — js/cart.js applies this for a moment after an add.
   Sage is the "used rarely" secondary accent; a quiet yes, not a celebration. */
.glow-add--added,
.glow-add--added:hover,
.glow-add--added:focus-visible {
  color: var(--glow-cream);
  background: var(--glow-sage);
  border-color: var(--glow-sage);
}

/* ---- Product detail ------------------------------------------------------ */

.glow-pdp { padding-block: clamp(2.5rem, 6vw, 5rem) var(--glow-section-y); }

.glow-pdp__crumb { margin-bottom: var(--glow-s4); }

.glow-pdp__grid {
  display: grid;
  gap: var(--glow-s5);
}

@media (min-width: 980px) {
  .glow-pdp__grid {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
    gap: var(--glow-s7);
    align-items: start;
  }
  .glow-pdp__gallery { position: sticky; top: 7rem; }
}

.glow-pdp__stage {
  display: grid;
  place-items: center;
  aspect-ratio: 1 / 1;
  background: var(--glow-blush);
  border-radius: var(--glow-radius);
  padding: 9%;
  margin: 0;
  overflow: hidden;
}

.glow-pdp__stage img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  mix-blend-mode: multiply;
}

.glow-pdp__thumbs {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

.glow-pdp__thumb {
  display: grid;
  place-items: center;
  width: 5rem;
  aspect-ratio: 1 / 1;
  background: var(--glow-blush);
  border-radius: var(--glow-radius);
  padding: 0.6rem;
  overflow: hidden;
}

.glow-pdp__thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  mix-blend-mode: multiply;
}

.glow-pdp__title {
  max-width: 16ch;
  margin-top: 0;
}

.glow-pdp__price {
  display: flex;
  align-items: baseline;
  gap: var(--glow-s2);
  font-family: var(--glow-font-display);
  font-size: var(--glow-t2);
  color: var(--glow-ink);
  margin: var(--glow-s2) 0 0;
}

.glow-pdp__size {
  font-family: var(--glow-font-body);
  font-size: var(--glow-small);
  color: var(--glow-muted);
}

.glow-pdp__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--glow-s3);
  margin-top: var(--glow-s4);
}

.glow-pdp__desc {
  margin-top: var(--glow-s5);
  padding-top: var(--glow-s4);
  border-top: 1px solid var(--glow-border);
}

.glow-pdp__desc p { max-width: 52ch; }

.glow-pdp__note {
  font-size: var(--glow-small);
  color: var(--glow-muted);
}

/* Cross-link to consultation. A pointer, not a pitch. */
.glow-pdp__consult {
  margin-top: var(--glow-s5);
  background: var(--glow-blush);
  border-radius: var(--glow-radius);
  padding: var(--glow-s4);
}

.glow-pdp__consult-title {
  font-family: var(--glow-font-display);
  font-size: var(--glow-t3);
  line-height: 1.3;
  color: var(--glow-ink);
  max-width: 26ch;
  margin: 0 0 var(--glow-s2);
}

/* ── Favourites (portal shop) ─────────────────────────────────────────────
   Added 31 Aug 2026 — Hemi: "I feel like they should be able to favourite
   them from here as well." Client-built list, localStorage like the cart;
   nothing fires from it. See templates/portal-shop.html for the doctrine
   bounds. The heart sits beside Add to cart in .glow-product__acts. */

.glow-product__acts {
  display: inline-flex;
  align-items: center;
  gap: var(--glow-s1);
}

.glow-fav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  font-size: 1.05rem;
  line-height: 1;
  color: var(--glow-muted);
  background: transparent;
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius-ui);
  cursor: pointer;
  transition: color var(--glow-dur) var(--glow-ease),
              border-color var(--glow-dur) var(--glow-ease);
}

.glow-fav:hover,
.glow-fav:focus-visible {
  color: var(--glow-burgundy);
  border-color: var(--glow-burgundy);
}

.glow-fav--on {
  color: var(--glow-burgundy);
  border-color: var(--glow-burgundy);
}

/* ── Portal shop control bar ──────────────────────────────────────────────
   Added 31 Aug 2026 with the arrange control and the cart chip — see
   templates/portal-shop.html. The bar reuses .glow-shop-filter/.glow-chip
   from the public shop; these rules are only what the portal context and
   the two new controls need. */

/* .glow-shop-filter's s6 top margin is sized for the public shop, where it
   sits below the hero. In the portal it is the first thing in the block. */
.glow-portal .glow-shop-filter { margin-top: 0; }

/* Anchored brand groups: land clear of the sticky portal chrome. */
.glow-sec__grp[data-brand] { scroll-margin-top: 6rem; }

/* Pushes the cart chip to the far end of the chip row. */
.glow-chip--push { margin-left: auto; }

/* An icon inside a chip. The chip aligns its children on the baseline for
   text + count; an SVG has no baseline worth using, so it centres itself.
   Same basket glyph as the portal rail — Hemi: "cant there be a picture
   of a cart or something", 31 Aug 2026. */
.glow-chip__icon { align-self: center; display: block; }

/* The cart count INSIDE a chip. .glow-cart-count is dressed for the dark
   header — a burgundy pill with cream text — and inside a cream chip that
   reads as a blob with an unreadable number (Hemi: "this can be better",
   31 Aug 2026). In chip context it undresses: a bare burgundy numeral,
   same scale as the other chip counts, no fill. (0,2,0) so it beats both
   single-class rules whatever the bundle order. */
.glow-chip .glow-cart-count {
  min-width: 0;
  padding: 0;
  background: transparent;
  border-radius: 0;
  color: var(--glow-burgundy);
  font-size: var(--glow-tag);
  font-weight: 500;
}

.glow-sort {
  font-family: var(--glow-font-body);
  /* 1rem, NOT the chip's 13px: iOS Safari zooms the whole page when a form
     control under 16px takes focus, and stays zoomed. Review, 31 Aug 2026. */
  font-size: 1rem;
  font-weight: 500;
  color: var(--glow-ink-soft);
  background: transparent;
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius-sm);
  padding: 0.4rem 0.75rem;
  cursor: pointer;
}
.glow-sort:hover,
.glow-sort:focus-visible {
  color: var(--glow-ink);
  border-color: var(--glow-ink-soft);
}

/* NO dividers in the catalogue — Hemi, 31 Aug 2026: "Still a line there."
   Whitespace separates the brand groups; the eyebrow does the naming. The
   selector is deliberately (0,4,0) so it beats _portal.css's generic
   `.glow-sec__grp + .glow-sec__grp` hairline whatever the bundle order. */
.glow-portal .glow-block--catalog > .glow-block__body > .glow-sec__grp {
  border-top: 0;
  padding-top: var(--glow-s5);
}
/* ==========================================================================
   Glow Cosmetique — cart and checkout
   Shells only: js/cart.js renders the state into #glow-cart-root and
   #glow-checkout-root. Same restraint as the shop — hairlines, blush media
   fields, no urgency furniture. The cart states what is in it and what it
   costs; nothing else.
   ========================================================================== */

.glow-cart-heading {
  font-size: var(--glow-h2);
  margin-bottom: clamp(2rem, 5vw, 3.5rem);
}

/* ---- Line items --------------------------------------------------------- */

.glow-cart__lines {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--glow-border);
}

.glow-cart-line {
  display: grid;
  grid-template-columns: clamp(4.25rem, 16vw, 5.5rem) minmax(0, 1fr) auto;
  gap: var(--glow-s3);
  align-items: center;
  padding-block: var(--glow-s3);
  border-bottom: 1px solid var(--glow-border);
}

.glow-cart-line__media {
  display: grid;
  place-items: center;
  aspect-ratio: 1 / 1;
  background: var(--glow-blush);
  border-radius: var(--glow-radius);
  padding: 0.6rem;
  overflow: hidden;
}

.glow-cart-line__media img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  mix-blend-mode: multiply;
}

.glow-cart-line__title {
  display: inline-block;
  font-family: var(--glow-font-display);
  font-size: var(--glow-t3);
  line-height: 1.25;
  color: var(--glow-ink);
  transition: color var(--glow-dur) var(--glow-ease);
}

.glow-cart-line__title:hover { color: var(--glow-burgundy); }

.glow-cart-line__unit {
  display: block;
  margin-top: 0.2rem;
  font-size: var(--glow-small);
  color: var(--glow-muted);
}

.glow-cart-line__controls {
  display: flex;
  align-items: center;
  gap: var(--glow-s3);
  margin-top: var(--glow-s2);
}

.glow-cart-line__total {
  font-size: var(--glow-body);
  color: var(--glow-ink);
  text-align: right;
  white-space: nowrap;
}

/* ---- Quantity stepper ---------------------------------------------------- */

.glow-qty {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius-sm);
  overflow: hidden;
}

.glow-qty__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--glow-font-body);
  font-size: var(--glow-small);
  color: var(--glow-burgundy);
  cursor: pointer;
  transition: background var(--glow-dur) var(--glow-ease);
}

.glow-qty__btn:hover:not(:disabled),
.glow-qty__btn:focus-visible:not(:disabled) { background: var(--glow-blush); }

.glow-qty__btn:disabled { color: var(--glow-border); cursor: default; background: none; border-color: transparent; }

.glow-qty__num {
  min-width: 1.8rem;
  text-align: center;
  font-size: var(--glow-small);
  color: var(--glow-ink);
}

.glow-cart-line__remove {
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--glow-font-body);
  font-size: var(--glow-small);
  color: var(--glow-muted);
  cursor: pointer;
  border-bottom: 1px solid transparent;
  transition: color var(--glow-dur) var(--glow-ease),
              border-color var(--glow-dur) var(--glow-ease);
}

.glow-cart-line__remove:hover,
.glow-cart-line__remove:focus-visible {
  color: var(--glow-burgundy);
  border-color: var(--glow-burgundy);
}

/* ---- Cart foot ----------------------------------------------------------- */

.glow-cart__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--glow-s3) var(--glow-s4);
  margin-top: var(--glow-s4);
}

.glow-cart__subtotal {
  display: flex;
  align-items: baseline;
  gap: var(--glow-s2);
}

.glow-cart__subtotal-label {
  font-size: var(--glow-small);
  color: var(--glow-muted);
}

.glow-cart__subtotal-amount {
  font-family: var(--glow-font-display);
  font-size: var(--glow-t1);
  color: var(--glow-ink);
}

/* ---- Empty state and pricing note ---------------------------------------- */

.glow-cart-empty {
  border-top: 1px solid var(--glow-border);
  padding-top: var(--glow-s4);
}

.glow-cart-empty__line {
  font-family: var(--glow-font-display);
  font-size: var(--glow-h3);
  color: var(--glow-ink);
  margin-bottom: var(--glow-s2);
}

.glow-cart-note {
  margin-top: var(--glow-s5);
  font-size: var(--glow-small);
  color: var(--glow-muted);
  max-width: 52ch;
}

/* ---- Checkout summary ---------------------------------------------------- */

.glow-summary { max-width: 40rem; }

.glow-summary__lines {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--glow-border);
}

.glow-summary__line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--glow-s3);
  padding-block: var(--glow-s2);
  border-bottom: 1px solid var(--glow-border);
}

.glow-summary__qty {
  font-size: var(--glow-small);
  color: var(--glow-muted);
}

.glow-summary__amount {
  color: var(--glow-ink);
  white-space: nowrap;
}

.glow-summary__total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--glow-s3);
  padding-block: var(--glow-s3);
  font-family: var(--glow-font-display);
  font-size: var(--glow-t3);
  color: var(--glow-ink);
}

.glow-summary__edit { margin-top: var(--glow-s2); }

/* ---- Checkout pending note ------------------------------------------------
   Builder note, deliberately visible: payment capture is pending the AU
   payments decision (notes/decisions.md, "Open"). Remove this block when the
   payment step is real. */

.glow-checkout-pending {
  margin-top: var(--glow-s5);
  max-width: 40rem;
  background: var(--glow-blush);
  border-radius: var(--glow-radius);
  padding: var(--glow-s4);
}

.glow-checkout-pending__label {
  font-size: var(--glow-eyebrow-size);
  font-weight: var(--glow-eyebrow-weight);
  letter-spacing: var(--glow-eyebrow-track);
  text-transform: uppercase;
  color: var(--glow-muted);
  margin: 0 0 var(--glow-s2);
}

.glow-checkout-pending__body {
  font-size: var(--glow-small);
  color: var(--glow-ink-soft);
  margin: 0;
  max-width: 52ch;
}


/* --------------------------------------------------------------------------
   Checkout form
   Same hairline restraint as the rest of the shop — no boxed "secure checkout"
   badges, no card-brand logos, no trust theatre. The card page is HubSpot's
   and says so.

   40rem matches .glow-summary directly above it. It was 34rem, written when
   this form was three fields; once delivery and an address were added, the
   summary's rule ran 96px wider than the form's and the two stacked blocks
   had visibly different right edges. 31 Aug 2026.
   -------------------------------------------------------------------------- */
.glow-checkout-form {
  margin-top: 2.5rem;
  padding-top: 2rem;
  border-top: 1px solid var(--glow-surface-border);
  max-width: 40rem;
}

.glow-checkout-form__heading {
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: .01em;
  margin: 0 0 .35rem;
}

.glow-checkout-form__note {
  margin: 0 0 1.5rem;
  font-size: .9rem;
  opacity: .72;
}

.glow-field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  align-items: start;
  margin-bottom: 1.15rem;
}

/* Suburb / State / Postcode. This row has THREE children and inherited the
   two-column rule, so postcode wrapped onto a second line and the row read
   as broken. 31 Aug 2026. */
.glow-field-row--addr { grid-template-columns: 2fr 1fr 1fr; }

/* _page.css:888 sets `.glow-field + .glow-field { margin-top }` for the
   stacked contact form. Inside a grid those are siblings too, so every
   second and third cell sat lower than the first — the ragged look. */
.glow-field-row > .glow-field { margin-top: 0; margin-bottom: 0; }

@media (max-width: 34rem) {
  .glow-field-row { grid-template-columns: 1fr; }
  /* Suburb takes the width; state and postcode are short and pair well. */
  .glow-field-row--addr { grid-template-columns: 1fr 1fr; }
  .glow-field-row--addr > .glow-field:first-child { grid-column: 1 / -1; }
}

.glow-field { margin-bottom: 1.15rem; }

.glow-field label {
  display: block;
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  margin-bottom: .4rem;
  opacity: .8;
}

.glow-field input,
.glow-field select {
  width: 100%;
  padding: .7rem .85rem;
  font: inherit;
  color: inherit;
  background: transparent;
  /* --glow-hairline was never declared anywhere, so this fell through to a
     PURE BLACK rule on a warm-cream palette where every other border is
     #E2D6C9. 30 Aug 2026. */
  border: 1px solid var(--glow-surface-border);
  border-radius: var(--glow-radius-ui);
}

.glow-field input:focus-visible,
.glow-field select:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 1px;
}

/* The state picker is the only <select> in the shop. Left native it renders
   ~99x25 next to 264x53 inputs in a system font — the single loudest thing
   on the page. Chevron is inline so it costs no request. */
.glow-field select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 2.4rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%237A6A62' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .95rem center;
}

.glow-field__hint {
  display: block;
  margin-top: .35rem;
  font-size: .8rem;
  opacity: .65;
}

.glow-checkout-error {
  margin: 0 0 1rem;
  padding: .75rem .9rem;
  font-size: .9rem;
  border: 1px solid currentColor;
  border-radius: 2px;
}

.glow-checkout-form__after {
  margin: 1rem 0 0;
  font-size: .85rem;
  opacity: .7;
  line-height: 1.55;
}

/* --------------------------------------------------------------------------
   Delivery choice
   Written 31 Aug 2026. The markup shipped the night before with no CSS, so
   both options ran together as one paragraph of prose and the two radios
   were indistinguishable from body text. Two options, one decision, stated
   at full price — the same hairline treatment as everything else.
   -------------------------------------------------------------------------- */
.glow-choice {
  display: grid;
  gap: .75rem;
  margin: 0 0 1.75rem;
}

.glow-choice__opt {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .85rem;
  align-items: start;
  padding: 1rem 1.1rem;
  border: 1px solid var(--glow-surface-border);
  border-radius: var(--glow-radius-ui);
  cursor: pointer;
}

/* Unchecked is the base state, so a browser without :has() still gets two
   legible bordered options — it just does not tint the chosen one. */
.glow-choice__opt:has(input:checked) {
  border-color: var(--glow-burgundy);
  background: var(--glow-blush);
}

.glow-choice__opt:has(input:focus-visible) {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

.glow-choice__opt input[type="radio"] {
  width: 1rem;
  height: 1rem;
  margin: .25rem 0 0;
  accent-color: var(--glow-burgundy);
}

.glow-choice__body { display: block; }

.glow-choice__title {
  display: block;
  font-weight: 600;
  margin-bottom: .2rem;
}

.glow-choice__desc {
  display: block;
  font-size: .875rem;
  line-height: 1.5;
  color: var(--glow-muted);
}

/* "Delivery" is a second step, not a subheading of the one above it. */
.glow-checkout-form__heading--step { margin-top: 2.25rem; }
/* ==========================================================================
   Glow Cosmetique — portal and auth screens
   ==========================================================================

   Two surfaces, one vocabulary:
     · Auth screens  — the four membership templates. Deep field, centred card.
     · Portal shell  — rail + top bar + content, behind the login.

   Structural discipline is borrowed from the Sigma Sync portal; the visual
   language is Glow's own. Specifically borrowed, because they are the details
   that separate a professional portal from a stock one:

     · The rail's logo well and the top bar are BOTH 56px, so their bottom
       borders form one unbroken line across the viewport. Do not change one
       without the other.
     · Elevation is a single 1px border. No shadows on content, ever.
     · The card's outer border is one step darker than the rules inside it
       (--glow-border outside, --glow-light within), so nested lists never
       compete with the card edge.
     · Active nav is a tint fill plus accent text — no accent bar, no weight
       change, no bold. Weight stays constant in all three states.

   Deliberately NOT borrowed: progress bars, percentages, streaks, unread
   counters. A clinical record is not a productivity app. See _portal.css
   "States" for what replaces them.

   Everything is .glow- prefixed and descendant-scoped, because HubSpot injects
   its settings stylesheet inline AFTER main.css and targets bare h1/a/p/button.
   ========================================================================== */


/* ==========================================================================
   AUTH SCREENS
   Contract: templates/partials/auth-shell.html
   ========================================================================== */

.glow-auth {
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--glow-deep);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--glow-s4);
  padding: var(--glow-s5) var(--glow-gutter);
}

.glow-auth__card {
  width: 100%;
  max-width: 27rem;
  background: var(--glow-cream);
  border-radius: var(--glow-radius);
  padding: clamp(2rem, 5vw, 3rem) clamp(1.5rem, 4vw, 2.5rem);
}

/* THE LOCKUP, AND THE ONE PLACE IT FITS.
   Hemi supplied Glow-Logo.webp on 5 Aug — 496x163, ink on transparent, plus a
   cream knockout. Ink extents measured rather than eyeballed:

       GLOW          rows 0–116   (72% of the height)
       gap           rows 117–148
       COSMETIQUE    rows 149–162 (8.6% of the height)

   A letterspaced small-caps line needs ~8px to read, so the lockup needs
   8 / 0.086 = ~93px of TOTAL height before its second line is legible. That is
   why it goes here and not in the rail: the rail well is 56px, which would put
   COSMETIQUE at 4.8px, and a wordmark with an illegible second line reads as a
   printing fault. The rail keeps its one-line Instrument Serif text mark, which
   is the right answer at that size rather than a stand-in.

   280px wide → 92px tall → COSMETIQUE at ~7.9px. The card is 27rem with up to
   2.5rem of padding, so ~352px of content width: it fits with air either side.

   The artwork is cropped hard to the top edge — row 0 carries ink — so it cannot
   be optically centred without space added here. */
.glow-auth__mark {
  display: block;
  width: min(17.5rem, 80%);
  height: auto;
  margin: 0 0 var(--glow-s5);
}

/* Fallback for anywhere the asset is absent. Also what the four membership
   templates rendered until 5 Aug, and still a legitimate identity. */
.glow-auth__mark--text {
  width: auto;
  font-family: var(--glow-font-display);
  font-size: 1.375rem;
  letter-spacing: 0.02em;
  color: var(--glow-ink);
}

.glow-auth__title {
  font-family: var(--glow-font-display);
  font-size: 2rem;
  line-height: 1.1;
  letter-spacing: -0.015em;
  color: var(--glow-ink);
  margin: 0 0 var(--glow-s2);
}

.glow-auth__intro {
  font-size: var(--glow-small);
  line-height: 1.6;
  color: var(--glow-muted);
  max-width: none;
  margin: 0 0 var(--glow-s4);
}

.glow-auth__foot {
  margin: var(--glow-s4) 0 0;
  padding-top: var(--glow-s3);
  border-top: 1px solid var(--glow-border);
  font-size: var(--glow-small);
  color: var(--glow-muted);
  max-width: none;
}

.glow-auth__foot a { color: var(--glow-burgundy); border-bottom: 1px solid transparent; }
.glow-auth__foot a:hover { border-color: var(--glow-burgundy); }

.glow-auth__base {
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(250, 247, 242, 0.4);
  margin: 0;
  max-width: none;
  text-align: center;
}

/* ---- The HubSpot form -----------------------------------------------------
   <span id="hs_cos_wrapper_" class="hs_cos_wrapper hs_cos_wrapper_widget hs_cos_wrapper_type_member_login" style="" data-hs-cos-general-type="widget" data-hs-cos-type="member_login" ><div class="hs-form-field">
  <ul class="no-list hs-error-msgs" style="text-align:center">
    <li>
      <label class="hs-membership-global-error hs-error-msg"></label>
    </li>
  </ul>
</div>
<form method="post" action="/_hcms/mem/login?portalId=441758719&templateId=359873765831&templatePath=Glow+Cosmetique%2Fcss%2Fmain.css&redirect_url=/" id="hs-membership-form" onsubmit="onFormSubmit()" data-hs-do-not-collect="">
  <input name="csrf_token" type="hidden" value=""/>
  <input name="redirect_url" type="hidden"value="/"/>
  <input id="hs-membership-form-hubspotutk" name="hubspotutk" type="hidden" />

  <div class="hs-form-field">
    <label class="hs-login-widget-email-label" for="hs-login-widget-email">Email*</label>
    <input class="hs-input" name="email"type="text"placeholder="Email"id="hs-login-widget-email"value=""/>
  </div>


  <div class="hs-form-field">
    <label class="hs-login-widget-password-label"for="hs-login-widget-password">Password*</label>
    <a class="hs-login-widget-show-password"href="javascript:show_password('hs-login-widget-password');">Show password</a>
    <input class="hs-input" name="password"type="password"placeholder="Password"id="hs-login-widget-password"/>
  </div>

  <div class="hs-form-field">
    <input class="hs-input" name="remember_me"type="checkbox"id="hs-login-widget-remember"value="true"checked>
    <label for="hs-login-widget-remember">Remember Me</label>
  </div>
  <div>
    <a id="hs_login_reset"href="/_hcms/mem/reset/request">Forgot your password?</a>
  </div>

  <input type="hidden" name="bot_challenger_token" value=""/>
<script>
(function() {
  var el = document.currentScript;
  while (el.previousElementSibling && el.previousElementSibling.tagName !== 'INPUT') {
    el = el.previousElementSibling;
  }
  var input = el.previousElementSibling;
  var form = input ? input.closest('form') : null;
  var ttlMs = 300 * 1000;
  var lastFetch = 0;
  var pending = false;

  function fetchToken() {
    if (pending) return;
    pending = true;
    var iframe = document.createElement('iframe');
    iframe.style.cssText = 'display:none';
    iframe.src = '/_hcms/mem/bot-challenger-token?action=' + encodeURIComponent('login');
    var timer = setTimeout(cleanup, 30000);
    function cleanup() {
      clearTimeout(timer);
      window.removeEventListener('message', onMessage);
      if (iframe.parentNode) document.body.removeChild(iframe);
      pending = false;
    }
    function onMessage(e) {
      if (e.source !== iframe.contentWindow) return;
      if (e.origin !== window.location.origin) return;
      if (!e.data || e.data.type !== 'bot-challenger-token') return;
      input.value = e.data.token;
      lastFetch = Date.now();
      cleanup();
    }
    window.addEventListener('message', onMessage);
    document.body.appendChild(iframe);
  }

  function refreshIfStale() {
    if (Date.now() - lastFetch >= ttlMs) fetchToken();
  }

  fetchToken();

  document.addEventListener('visibilitychange', function() {
    if (document.visibilityState === 'visible') refreshIfStale();
  });
  window.addEventListener('focus', refreshIfStale);
  if (form) {
    form.addEventListener('focusin', refreshIfStale);
    form.addEventListener('pointerdown', refreshIfStale);
  }
})();
</script>

  
  <div class="hs-membership-loader hs_submit hs-submit">
    <div class="actions">
      <input type="submit" class="hs-button primary large" value="Login"/>
    </div>
  </div>
</form>

<span class="form-separator">or</span>
<form method="post" action="/_hcms/mem/login/passwordless-auth/request?portalId=441758719" id="hs-membership-passwordless-auth-form" onsubmit="onFormSubmit()" data-hs-do-not-collect="">
  <input name="redirect_url" type="hidden"value="/"/>
  <div>
    <p>Try going password-less. Get a link sent to your email that'll sign you in.</p>
  </div>
  <div class="hs-form-field">
    <label class="hs-login-widget-email-label" for="hs-login-widget-email">Email*</label>
    <input class="hs-input" name="email"type="text"placeholder="Email"id="hs-login-widget-email"value=""/>
  </div>

  <div class="hs-form-field">
    <input class="hs-input" name="explicit_consent" type="checkbox" id="hs-passwordless-auth-explicit-consent" value="true">
    <label id="hs-passwordless-auth-checkbox-consent" for="hs-passwordless-auth-explicit-consent">I agree to allow Glow Cosmetique to store and process my personal data.</label>
  </div>

  <input type="hidden" name="bot_challenger_token" value=""/>
<script>
(function() {
  var el = document.currentScript;
  while (el.previousElementSibling && el.previousElementSibling.tagName !== 'INPUT') {
    el = el.previousElementSibling;
  }
  var input = el.previousElementSibling;
  var form = input ? input.closest('form') : null;
  var ttlMs = 300 * 1000;
  var lastFetch = 0;
  var pending = false;

  function fetchToken() {
    if (pending) return;
    pending = true;
    var iframe = document.createElement('iframe');
    iframe.style.cssText = 'display:none';
    iframe.src = '/_hcms/mem/bot-challenger-token?action=' + encodeURIComponent('passwordless');
    var timer = setTimeout(cleanup, 30000);
    function cleanup() {
      clearTimeout(timer);
      window.removeEventListener('message', onMessage);
      if (iframe.parentNode) document.body.removeChild(iframe);
      pending = false;
    }
    function onMessage(e) {
      if (e.source !== iframe.contentWindow) return;
      if (e.origin !== window.location.origin) return;
      if (!e.data || e.data.type !== 'bot-challenger-token') return;
      input.value = e.data.token;
      lastFetch = Date.now();
      cleanup();
    }
    window.addEventListener('message', onMessage);
    document.body.appendChild(iframe);
  }

  function refreshIfStale() {
    if (Date.now() - lastFetch >= ttlMs) fetchToken();
  }

  fetchToken();

  document.addEventListener('visibilitychange', function() {
    if (document.visibilityState === 'visible') refreshIfStale();
  });
  window.addEventListener('focus', refreshIfStale);
  if (form) {
    form.addEventListener('focusin', refreshIfStale);
    form.addEventListener('pointerdown', refreshIfStale);
  }
})();
</script>

  
  <div class="hs-membership-loader hs_submit hs-submit">
    <div class="actions">
      <input id="hs-passwordless-submit" type="submit" class="hs-button primary large" value="Email link"  />
    </div>
  </div>
</form>
<script>
  document.getElementById('hs-passwordless-submit').disabled = true;
  document.querySelector("input[name=explicit_consent]").addEventListener('change',function(){
    if (this.checked){
      document.getElementById('hs-passwordless-submit').disabled=false;
    } else{
      document.getElementById('hs-passwordless-submit').disabled=true;
    }
  });
</script>

<script type="text/javascript">
  function onFormSubmit() {
//    document.querySelector('.hs-membership-loader').classList.add('active');
  }

  document.onkeydown = function(e) {
    if (['ArrowUp','ArrowDown'].includes(e.code)) {
      var children = [].slice.call(document.querySelectorAll('#hs-membership-form input:not([type="hidden"]):not([type="checkbox"]):not([disabled])'));
      for (i = 0; i < children.length; i++) {
        var input = children[i];
        if (input === document.activeElement) {
          if (e.code =='ArrowDown' && children[i+1] !== undefined) {
            children[i+1].focus();
            break;
          } else if (e.code=='ArrowUp' && children[i-1]!==undefined) {
            children[i-1].focus();
            break;
          }
        } else if (i + 1 === children.length) {
          children[0].focus();
        }
      }
    } else if ('Enter' === e.code) {
      e.preventDefault();
      var children = [].slice.call(document.querySelectorAll('#hs-membership-form input:not([type="hidden"]):not([type="checkbox"]):not([disabled])'));
      for (i = 0; i < children.length; i++) {
        var input=children[i];
        if (input ===  document.activeElement && children[i+1] !== undefined) {
          children[i+1].focus();
          break;
        } else if (input ===  document.activeElement && i + 1 === children.length) {
//          document.querySelector('.hs-membership-loader').classList.add('active');
          document.getElementById('hs-membership-form').submit();
        } else if (i + 1 === children.length) {
          children[0].focus();
        }
      }
    }
  }
</script>


<div id="hs-membership-rate-limit-error-text" style="display:none">
  You've made too many attempts at this request. Please try this action again in a few minutes.
</div>
<script type="text/javascript">
  function show_password(id) {
    var input = document.getElementById(id);
    input.type = input.type === 'password' ? 'text' : 'password';
  }
</script>
<script>
  function getCookie(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for(var i=0;i < ca.length;i++) {
      var c = ca[i];
      while (c.charAt(0)==' ') c = c.substring(1,c.length);
      if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
    }
    return null;
  }

  function setTracker() {
    var utkInput = document.getElementById('hs-membership-form-hubspotutk');
    if (!utkInput.value) {
      utkInput.value = getCookie("hubspotutk");
    }
  }

  setInterval(setTracker, 300);
</script>
</span> and friends are HubL tags, not modules — their markup is
   fixed and cannot be restructured. These ids and classes are the only handles
   they expose. Restyle freely; restructure not at all. */

.glow-auth__form .hs-form-field { margin-bottom: var(--glow-s3); }

.glow-auth__form label {
  display: block;
  font-family: var(--glow-font-body);
  font-size: var(--glow-tag);
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--glow-muted);
  margin-bottom: 0.5rem;
}

.glow-auth__form input[type="email"],
.glow-auth__form input[type="password"],
.glow-auth__form input[type="text"] {
  width: 100%;
  box-sizing: border-box;
  font-family: var(--glow-font-body);
  font-size: var(--glow-body);
  color: var(--glow-ink);
  background: var(--glow-white);
  border: 1px solid var(--glow-surface-border);
  border-radius: var(--glow-radius-ui);
  padding: var(--glow-field-pad);
  transition: border-color var(--glow-dur) var(--glow-ease);
}

.glow-auth__form input[type="email"]:focus,
.glow-auth__form input[type="password"]:focus,
.glow-auth__form input[type="text"]:focus {
  outline: none;
  border-color: var(--glow-burgundy);
}

/* Remember-me is a checkbox and must not inherit the block label rule above. */
.glow-auth__form input[type="checkbox"] {
  margin-right: 0.5rem;
  /* Was the browser default — a bright blue tick on a cream and burgundy
     screen, the only blue in the product. _intake.css has set this since 8 Aug;
     the auth screens never got it. 30 Aug 2026. */
  accent-color: var(--glow-action);
}

/* HubSpot emits the checkbox BEFORE its label, for both remember-me and the
   passwordless consent. The adjacent-sibling rule catches every one of them,
   including any HubSpot adds later.

   Casing matters more than it looks here: the consent label is a legal
   statement ("I agree to allow Glow Cosmetique to store and process my personal
   data"), and the uppercase field-label treatment rendered it as two lines of
   11px small caps — the least readable form of the one sentence on this screen
   that has to be readable. */
.glow-auth__form input[type="checkbox"] + label {
  display: inline;
  font-size: var(--glow-small);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--glow-ink-soft);
}

/* "Show password" is emitted between the label and the input, so it read as a
   heading for the field rather than a control on it. Reorder visually; the DOM
   is fixed and cannot be touched. Scoped to password fields so the checkbox
   rows, which are not column-flex, are unaffected. */
.glow-auth__form .hs-form-field:has(input[type="password"]) {
  display: flex;
  flex-direction: column;
}

.glow-auth__form .hs-form-field:has(input[type="password"]) > label { order: 1; }
.glow-auth__form .hs-form-field:has(input[type="password"]) > input { order: 2; }

.glow-auth__form .hs-form-field:has(input[type="password"]) .hs-login-widget-show-password {
  order: 3;
  align-self: flex-end;
  margin-top: 0.4rem;
}

.glow-auth__form .hs-button,
.glow-auth__form input[type="submit"] {
  width: 100%;
  font-family: var(--glow-font-body);
  font-size: var(--glow-small);
  font-weight: 500;
  color: var(--glow-cream);
  background: var(--glow-burgundy);
  border: 1px solid var(--glow-burgundy);
  border-radius: var(--glow-radius-ui);
  padding: 0.9rem 1.75rem;
  line-height: 1.2;
  cursor: pointer;
  transition: background var(--glow-dur) var(--glow-ease),
              border-color var(--glow-dur) var(--glow-ease);
}

.glow-auth__form .hs-button:hover,
.glow-auth__form input[type="submit"]:hover {
  background: var(--glow-deep);
  border-color: var(--glow-deep);
}

.glow-auth__form .actions { margin-top: var(--glow-s3); }

.glow-auth__form #hs_login_reset,
.glow-auth__form .hs-login-widget-show-password,
.glow-auth__form #hs-login-self-registration-link a {
  display: inline-block;
  margin-top: var(--glow-s2);
  font-size: var(--glow-small);
  color: var(--glow-burgundy);
  border-bottom: 1px solid transparent;
}

.glow-auth__form #hs_login_reset:hover,
.glow-auth__form .hs-login-widget-show-password:hover,
.glow-auth__form #hs-login-self-registration-link a:hover {
  border-color: var(--glow-burgundy);
}

/* Passwordless: whether it appears is a HubSpot setting, not a CSS decision.
   The agency theme hid it with display:none while leaving the endpoint live —
   that is not disabling it. Styled here so that if it IS on, it looks right. */
.glow-auth__form .form-separator {
  display: block;
  margin: var(--glow-s3) 0;
  font-size: var(--glow-small);
  color: var(--glow-muted);
  text-align: center;
}

/* Errors: amber, never red. A failed sign-in is not a failure state. */
.glow-auth__form .hs-error-msgs,
.glow-auth__form .hs-error-msg,
.glow-auth__form .form-input-validation-message {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
  font-size: var(--glow-small);
  color: #8A5A2B;
}


/* ==========================================================================
   PORTAL SHELL
   ========================================================================== */

/* The frame does not scroll; only the content column does. Rail and top bar
   stay put without position:fixed. */
.glow-portal {
  display: flex;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  background: var(--glow-cream);
}

/* ---- Rail ---------------------------------------------------------------- */

.glow-rail {
  flex: none;
  width: 15rem;              /* 240px */
  background: var(--glow-white);
  border-right: 1px solid var(--glow-border);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: width var(--glow-dur) var(--glow-ease);
}

.glow-portal[data-rail="collapsed"] .glow-rail { width: 4rem; }  /* 64px */

/* The seam. This well is the same 56px as the top bar and carries the same
   bottom border, so the two form one continuous line across the viewport.
   Change one, change both. */
.glow-rail__well {
  flex: none;
  height: 56px;
  display: flex;
  align-items: center;
  padding-inline: var(--glow-s2);
  border-bottom: 1px solid var(--glow-border);
}

.glow-rail__mark {
  font-family: var(--glow-font-display);
  font-size: 1.125rem;
  letter-spacing: 0.02em;
  color: var(--glow-ink);
  white-space: nowrap;
  overflow: hidden;
}

/* Collapsed, the well is 64px and "Glow Cosmetique" truncated to "Glov" —
   a cut-off wordmark is the single most amateur thing a collapsed rail can do.
   Swap the full wordmark for a monogram instead of clipping it. */
.glow-rail__mark-short { display: none; }

.glow-portal[data-rail="collapsed"] .glow-rail__well { justify-content: center; padding-inline: 0; }
.glow-portal[data-rail="collapsed"] .glow-rail__mark-full { display: none; }
.glow-portal[data-rail="collapsed"] .glow-rail__mark-short { display: block; font-size: 1.375rem; }

.glow-rail__nav {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--glow-s3) 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}

.glow-rail__link {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.65rem 0.75rem;
  border-radius: var(--glow-radius);
  font-size: var(--glow-small);
  font-weight: 500;              /* constant across all three states */
  color: var(--glow-muted);
  white-space: nowrap;
  transition: background var(--glow-dur) var(--glow-ease),
              color var(--glow-dur) var(--glow-ease);
}

.glow-rail__link:hover {
  background: var(--glow-cream);
  color: var(--glow-ink);
}

/* Active: tint fill + accent text. No bar, no bold, no icon change. */
.glow-rail__link[aria-current="page"] {
  background: var(--glow-blush);
  color: var(--glow-burgundy);
}

.glow-rail__icon { flex: none; width: 1.125rem; height: 1.125rem; }

.glow-portal[data-rail="collapsed"] .glow-rail__link { justify-content: center; padding-inline: 0.5rem; }
.glow-portal[data-rail="collapsed"] .glow-rail__label { display: none; }

/* ---- Rail footer --------------------------------------------------------- */

.glow-rail__foot {
  flex: none;
  padding: 0.75rem 0.5rem;
  border-top: 1px solid var(--glow-border);
}

/* The clinic, in the rail.

   Glow's portal has four pages where the Sigma portal has fourteen, so the rail
   has ~500px of dead column below the nav. Padding the nav with links to pages
   that do not exist would be faking it. This is the honest filler: static
   contact detail the client pulls when they want it.

   It is not a nudge. Nothing here contacts the client, scores them or prompts
   them — it answers "how do I ask about this?", which is the question a clinical
   record actually provokes. Sits at the bottom of the nav flow, above the
   attribution, and disappears entirely when the rail is collapsed. */
.glow-rail__help {
  margin-top: auto;
  padding: var(--glow-s3) 0.75rem var(--glow-s2);
}

.glow-rail__help-label {
  /* Type comes from the eyebrow group (8 Aug) — "THE CLINIC" in the rail and
     "YOUR RESULTS" in the page sat 60px apart at the same 11px with different
     weight and tracking, which is the kind of near-miss that reads as sloppy
     without anyone being able to say why. Only the margin is local now. */
  margin: 0 0 0.5rem;
}

.glow-rail__help-phone {
  display: block;
  font-size: var(--glow-small);
  font-weight: 500;
  color: var(--glow-ink);
  border-bottom: 1px solid transparent;
  margin-bottom: 0.35rem;
}

.glow-rail__help-phone:hover { border-color: var(--glow-border); }

.glow-rail__help-hours {
  font-size: var(--glow-meta);
  line-height: 1.5;
  color: var(--glow-muted);
  margin: 0;
  max-width: none;
}

.glow-portal[data-rail="collapsed"] .glow-rail__help { display: none; }

/* Attribution at a whisper — present and findable, never competing. */
.glow-rail__credit {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  padding: var(--glow-s3) 0;
  margin-bottom: 0.5rem;
  border-bottom: 1px solid var(--glow-border);
  text-align: center;
}

.glow-rail__credit-label {
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(122, 106, 98, 0.4);
}

.glow-rail__credit a {
  font-size: var(--glow-tag);
  letter-spacing: 0.04em;
  color: var(--glow-muted);
  opacity: 0.55;
  transition: opacity var(--glow-dur) var(--glow-ease);
}

.glow-rail__credit a:hover { opacity: 1; }

/* The whole block, not its children. Hiding only the label and link left the
   padding and the bottom border behind — an empty bordered strip above the
   collapse button, which reads as a rendering fault. */
.glow-portal[data-rail="collapsed"] .glow-rail__credit { display: none; }

.glow-rail__toggle {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  padding: 0.65rem 0.75rem;
  border: 0;
  border-radius: var(--glow-radius-ui);
  background: none;
  font-family: var(--glow-font-body);
  font-size: var(--glow-small);
  font-weight: 500;
  color: var(--glow-muted);
  cursor: pointer;
  text-align: left;
}

.glow-rail__toggle:hover { background: var(--glow-cream); color: var(--glow-ink); }
.glow-portal[data-rail="collapsed"] .glow-rail__toggle { justify-content: center; padding-inline: 0.5rem; }

/* ---- Top bar ------------------------------------------------------------- */

.glow-portal__main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.glow-topbar {
  flex: none;
  height: 56px;                       /* matches .glow-rail__well exactly */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--glow-s3);
  padding-inline: var(--glow-s3);
  background: var(--glow-white);
  border-bottom: 1px solid var(--glow-border);
}

.glow-topbar__title {
  font-size: var(--glow-small);
  font-weight: 500;
  color: var(--glow-ink);
  white-space: nowrap;
}

.glow-topbar__right {
  display: flex;
  align-items: center;
  gap: var(--glow-s3);
}

.glow-topbar__who {
  font-size: var(--glow-small);
  color: var(--glow-muted);
  white-space: nowrap;
}

.glow-topbar__phone {
  font-size: var(--glow-small);
  font-weight: 500;
  color: var(--glow-ink);
  white-space: nowrap;
}

.glow-topbar__signout {
  font-size: var(--glow-small);
  color: var(--glow-muted);
  border-bottom: 1px solid transparent;
  white-space: nowrap;
}

.glow-topbar__signout:hover { color: var(--glow-ink); border-color: var(--glow-border); }

.glow-burger--portal {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

.glow-burger--portal span {
  display: block;
  height: 1px;
  width: 100%;
  background: var(--glow-ink);
}

/* ---- Content column ------------------------------------------------------ */

/* THE GROUND IS DEEPER THAN THE CARDS, and that is the whole of what "contained
   in bubbles" means. Page --glow-cream (#FAF7F2) against white cards is a ~2%
   luminance step: technically two surfaces, visually one, so nothing ever reads
   as an object sitting ON something. MPS runs roughly #F5F5F4 under white and
   its cards lift immediately.

   Deepening the ground rather than adding shadow keeps the rule at the top of
   this file intact — elevation is a 1px border, no shadows on content, ever. */
.glow-portal__content {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  background: var(--glow-pt-ground);
  /* THE PAGE LINE. Was a bare clamp() here — the same number nothing else could
     reference, which is why ground text, card edges and card content each found
     their own left edge. Tokenised 30 Aug 2026, B-08 §0. */
  padding: var(--glow-page-pad);
}

/* The cap is PER PAGE, not baked into the shell. Sigma's shell is
   <main className="flex-1 overflow-y-auto"> with no cap at all; individual
   pages apply max-w-3xl (768px) or max-w-2xl (672px) where the content is
   document-like, and leave the dashboard and list pages full width to fill with
   card grids. Capping in the shell forces every future page into a reading
   column, which will be wrong for the results list the moment it exists.

   Set `portal_width` in the page template: "reading" (default) | "wide". */
.glow-portal__inner { max-width: none; }
.glow-portal__inner--reading { max-width: 48rem; }   /* 768px — Sigma's max-w-3xl */
/* 992px. For the result record: wide enough that a tabular row breathes, narrow
   enough that a range bar's two ends stay in one glance. Full width stretched a
   bar to ~700px and the marker stopped reading against its own limits. */
.glow-portal__inner--doc { max-width: 62rem; }

/* Every portal page needs one h1 for structure, but the top bar already states
   where you are — printing the same words again 140px below it is redundancy,
   not hierarchy. Pages whose heading would duplicate the top bar use this. */
.glow-portal__h1-sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.glow-portal__title {
  font-family: var(--glow-font-display);
  font-size: var(--glow-pt-h1);
  line-height: 1.15;
  letter-spacing: -0.015em;
  color: var(--glow-ink);
  margin: 0 0 var(--glow-s4);
}

/* ── THE HEADING GUARD — 8 Aug 2026 ─────────────────────────────────────────
   _base.css sets h1–h4 to the display serif for the PUBLIC site. In the portal
   the serif ends at the page title, so every lower heading level is forced back
   to Inter here rather than editing _base (the public site keeps its serif).

   ⚠ FONT-FAMILY ONLY, deliberately. This selector is 0,1,1 and BEATS every
   single-class rule in this file — put weight, size or tracking here and it
   silently overrides .glow-block__t's 0.11em, .glow-card__title's 600, and
   whatever comes next. That is the same specificity trap as the
   .glow-card__body p rule (see the :not([class]) note further down), and it is
   why h1 is EXCLUDED — including it would beat .glow-portal__title's serif. */
.glow-portal h2,
.glow-portal h3,
.glow-portal h4 {
  font-family: var(--glow-font-body);
}

/* The portal lead rides the prose token, not the public clamp. Inherited,
   .glow-lead is 17–19px viewport-dependent — the largest body text in the
   portal on every page, one step above prose for no reason a reader could
   name. 8 Aug: same size as prose; being first under the h1 is its rank. */
.glow-portal .glow-lead {
  font-size: var(--glow-pt-prose);
  line-height: 1.55;
}

/* A heading and the lead that belongs to it are ONE unit. Without this the h1's
   own 2rem bottom margin pushes the lead away from it, and the lead reads as an
   orphaned paragraph rather than as the subtitle. */
.glow-portal__title:has(+ .glow-lead) { margin-bottom: var(--glow-s2); }

/* The lead keeps .glow-lead's own 48ch measure — capped on WIDE pages too,
   because prose wants a measure even where the page below it fills the field
   with cards. This adds only the gap down to the first section. */
.glow-portal__title + .glow-lead { margin-bottom: var(--glow-s5); }

/* Space above EVERY section, not just between siblings. The adjacent-sibling
   form left the first section jammed against whatever preceded it — on the
   archetype page that is the panel, and the eyebrow landed 11px under it.
   Zeroed for a section that opens the page, where the h1 already sets the gap. */
.glow-portal__section { margin-top: var(--glow-s5); }
.glow-portal__section:first-child { margin-top: 0; }

/* ── THE EYEBROW. One definition, everywhere. ────────────────────────────────
   Normalized 5 Aug 2026. There were SIX uppercase-label treatments across this
   file — .glow-block__t, .glow-portal__eyebrow, .glow-sec__grp-t,
   .glow-labnote__t, .glow-relby__k, .glow-dochdr .k, .glow-whois__facts b —
   at 10px/11px/12px, weights 500 and 600, and tracking from 0.06em to 0.18em.

   Nobody chose six. Each was written next to the component it labels, each was
   reasonable in isolation, and the differences are only visible when you put
   two sections beside each other — which is exactly what the overview does.

   The composed rule is `.glow-eyebrow`; the historic class names are kept as
   aliases so no template has to change to get the fix. COLOUR stays with the
   component (muted by default, burgundy on a lab note), because colour carries
   meaning here and size does not.

   Section headers render whether or not the section has content. That single
   rule is why zero-state pages read as structured rather than broken. */
.glow-eyebrow,
.glow-portal__eyebrow,
.glow-sec__grp-t,
.glow-labnote__t,
.glow-relby__k,
.glow-dochdr .k,
.glow-block__t,
.glow-rail__help-label,
.glow-whois__facts b {
  font-family: var(--glow-font-body);
  /* Shared with the public site since 8 Aug — see --glow-eyebrow-* in
     _tokens.css. One definition, both surfaces; revert is two values there. */
  font-size: var(--glow-eyebrow-size);
  font-weight: var(--glow-eyebrow-weight);
  letter-spacing: var(--glow-eyebrow-track);
  text-transform: uppercase;
  color: var(--glow-muted);
}

.glow-portal__eyebrow { margin: 0 0 var(--glow-s3); }


/* Two columns on the wide pages, one on reading pages and mobile. Sigma fills
   its dashboard this way — cards, not a longer column.

   The container is __inner, NOT the grid itself. A container query cannot style
   its own container, so `container-type` and the queried rule on the same
   element silently does nothing — which is exactly what happened here the first
   time, and to the archetype panel before it. If a grid ever fails to split,
   this is the first thing to check. */
.glow-portal__inner { container-type: inline-size; }

.glow-portal__grid {
  display: grid;
  gap: var(--glow-s3);
}

@container (min-width: 40rem) {
  .glow-portal__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* A tile is a card you can enter. The whole thing is the target, so the arrow
   is an affordance rather than the hit area. */
.glow-tile {
  display: flex;
  flex-direction: column;
  gap: var(--glow-s2);
  background: var(--glow-white);
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius);
  padding: var(--glow-s3);
  transition: border-color var(--glow-dur) var(--glow-ease);
}

a.glow-tile:hover { border-color: var(--glow-muted); }

.glow-tile__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--glow-s2);
}

/* ── THE TITLE. One treatment for every card, tile and section. ──────────────
   5 Aug: four sizes within 4px were collapsed to one (20px serif).
   8 AUG: the serif itself goes. Hemi, with Inside Out and SigmaSync open
   beside this portal: both references are one-typeface sans systems, and the
   20px serif interleaved with Inter at card range was the named "mess". Serif
   now lives at the page h1 and the rail wordmark only; a title is Inter 600 at
   prose size — one weight step up, which is how both references carry rank.

   letter-spacing is EXPLICIT because the element type must not matter: as
   inherited values, h2 carried -0.015em, h3 -0.005em and spans 0 — three
   trackings on visually identical titles. */
.glow-tile__title,
.glow-card__title,
.glow-sec__t {
  font-family: var(--glow-font-body);
  font-size: var(--glow-pt-title);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.3;
  color: var(--glow-ink);
  margin: 0;
}

.glow-tile__body {
  font-size: var(--glow-small);
  line-height: 1.6;
  color: var(--glow-muted);
  margin: 0;
  max-width: none;
}

/* An optional fact under the body — a date, a position. Set in ink rather than
   muted so it reads as the tile's one piece of real content, and separated by a
   hairline so it does not run on from the description as another sentence.

   A fact, never a figure. Nothing computed, nothing compared. */
/* The FACT — the right-hand answer on a tile. Label left, fact right, the same
   as the results row, the span row and the price table. Before 30 Aug the right
   of a tile held a status pill (two of them read "READY", which is not
   information) and the fact was either a caption underneath or absent entirely.

   Tabular numerals so dates and counts hold a column when the tiles sit in a
   grid — the thing that makes Hemi's preferred block read tight. */
.glow-tile__fact {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
  gap: 0.15rem;
  margin-left: auto;
  min-width: 0;
}
.glow-tile__fact b {
  font-size: var(--glow-pt-row);
  font-weight: 600;
  color: var(--glow-text);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.glow-tile__fact em {
  font-style: normal;
  font-size: var(--glow-tag);
  color: var(--glow-text-muted);
  line-height: 1.3;
}
/* With a fact present the body is supporting detail, not the headline. */
.glow-tile--fact .glow-tile__body { color: var(--glow-text-muted); }

.glow-tile__meta {
  margin: 0;
  padding-top: var(--glow-s2);
  border-top: 1px solid var(--glow-border);
  font-size: var(--glow-meta);
  letter-spacing: 0.01em;
  color: var(--glow-ink-soft);
}

/* Pushes the link to the bottom so tiles of different text lengths still line
   their actions up. */
.glow-tile__foot { margin-top: auto; padding-top: var(--glow-s2); }


/* ==========================================================================
   CARDS AND STATES
   ========================================================================== */

.glow-card {
  background: var(--glow-white);
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius);
}

/* A SECOND surface weight, and the portal needs one. Every card being white on
   cream with the same hairline gives a page no hierarchy — it reads as a stack
   of equal claims, which is what made these pages feel soft. Explanatory asides
   ("what this is", "why the order matters") sit on the tinted ground so the
   client's OWN record stays the white, foreground surface.

   Tint only. No shadow, no gradient — composition does the work here. */
.glow-card--soft {
  background: var(--glow-blush);
  border-color: transparent;
}

.glow-card--soft .glow-card__body p { color: var(--glow-ink-soft); }
.glow-card--soft .glow-note { border-left-color: rgba(109, 41, 50, 0.25); }

/* --glow-light is the divider on a WHITE card; on the blush ground it is very
   nearly the ground itself and the rows read as one block. One step darker. */
.glow-card--soft .glow-card__rows > * + * { border-top-color: var(--glow-border); }

/* position:relative anchors the "?" explainer panel; the flex row is what keeps
   the "?" on the title's baseline instead of dropping to its own line. */
.glow-card__head {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--glow-s2);
  padding: var(--glow-head-y) var(--glow-s3);
  border-bottom: 1px solid var(--glow-border);
}

/* Serif, because a card title is a heading and Instrument Serif IS the brand —
   a portal set entirely in 14px Inter throws away the identity every public
   page spends its budget establishing.
   Size now comes from the shared title token, with .glow-tile__title and
   .glow-sec__t — see the note above .glow-tile__title. */

.glow-card--soft .glow-card__head { border-bottom-color: var(--glow-border); }

.glow-card__body { padding: var(--glow-s3); }

/* Cards in a grid stretch to the tallest sibling, so a short card's action would
   float mid-air. Pushes the last note or link to the bottom edge. */
.glow-card { display: flex; flex-direction: column; }
.glow-card__body { flex: 1; }

/* Reading prose inside a card. A paragraph a client actually reads and a label
   they scan are different jobs, so prose sits a step above the 14px workhorse —
   but ONE step, at --glow-pt-prose. It was 17px, which at this density read as
   a different document interleaved with the page.

   ⚠ :not([class]) IS LOAD-BEARING. Written bare, `.glow-card__body p` has
   specificity 0,1,1 and BEAT every single-class paragraph rule in this file —
   .glow-eyebrow, .glow-note and .glow-labquote are all 0,1,0. So an eyebrow
   inside a card rendered at 17px instead of 11px, and a note at 17px instead
   of 14px, no matter what their own rules said.

   That is the whole "the portal has too many text sizes" complaint: the sizes
   were declared correctly and silently overridden. The 5 Aug normalisation
   could not have found it by counting tokens, because every token was right.

   The rule now: a paragraph carrying a class owns its own type. An unclassed
   paragraph is prose and gets the prose size. */
.glow-card__body p:not([class]) {
  font-size: var(--glow-pt-prose);
  /* 1.65 across 68ch was the clunk. Shorter measure, tighter leading — a card
     paragraph is three lines of support, not an article. 30 Aug 2026. */
  line-height: 1.55;
  color: var(--glow-text-soft);
  max-width: 58ch;
}

.glow-card__body p + p { margin-top: var(--glow-s3); }

/* Rules WITHIN a card are one step lighter than the card's own edge, so nested
   lists never compete with the outline. */
.glow-card__rows > * + * { border-top: 1px solid var(--glow-light); }

.glow-card__row {
  display: flex;
  align-items: center;
  gap: var(--glow-s3);
  padding: var(--glow-s2) var(--glow-s3);
  transition: background var(--glow-dur) var(--glow-ease);
}

a.glow-card__row:hover { background: var(--glow-cream); }

.glow-card__row-title { font-size: var(--glow-small); font-weight: 500; color: var(--glow-ink); }

/* Definition rows — label column fixed so the values share a left edge. Without
   it the values start wherever their label happened to end, which reads as
   ragged rather than tabular. Opt-in, because the results list wants its title
   to take the space instead. Collapses on narrow containers. */
.glow-card__rows--spec .glow-card__row-title { flex: 0 0 10rem; }

@container (max-width: 30rem) {
  .glow-card__rows--spec .glow-card__row { flex-direction: column; gap: 0.25rem; align-items: start; }
  .glow-card__rows--spec .glow-card__row-title { flex: none; }
}
/* Meta sits at the RIGHT edge of the row. Fixed 5 Aug 2026, reported by Hemi
   against the review page.

   Only `__row-arrow` carried `margin-left: auto`, so a row with an arrow pushed
   its trailing element right and a row WITHOUT one did not — the meta simply sat
   against the title with 400px of empty row beside it. Measured: title at 305,
   meta at 454, row 990 wide.

   Every page that pairs a title with a status chip is the no-arrow case, so this
   was every status row in the portal, not an edge case.

   Spec rows opt out: they are label/value definition pairs whose values share a
   left edge on purpose, and pushing the value to the far side of the card
   un-tabulates exactly what --spec exists to line up. */
/* ── THE ROUTINE-LEVEL INSTRUCTION.
   Added 5 Aug 2026 — Hemi: "what's going on here?" against a card whose intro
   paragraph sat flush to the card edge at lead size while the rows below it
   were indented 32px.

   Cause: `.glow-block .glow-card__body { padding: 0 }` (line ~1754). That is
   correct for a card inside a block — the block supplies the padding — but it
   means a card that ALSO has its own body loses it, and the paragraph fell to
   the card's outer edge while the rows kept their own.

   The real answer is that this content was never a card body. It is a rule that
   governs every row beneath it — "no makeup the following day, no actives for
   seven days" — so it is an INTRO ROW: same 32px inset as the rows, divided
   from them, and set at row-title weight rather than as a lead paragraph. A
   lead-sized sentence above a 14px list inverts the hierarchy of the card, since
   the instruction is context and the steps are the subject. */
.glow-card__intro {
  padding: var(--glow-s2) var(--glow-s3);
  margin: 0;
  font-size: var(--glow-small);
  line-height: 1.55;
  color: var(--glow-ink-soft, var(--glow-ink));
  border-bottom: 1px solid var(--glow-light);
  background: var(--glow-cream);
}

.glow-card__row-meta { font-size: var(--glow-small); color: var(--glow-muted); margin-left: auto; }
.glow-card__rows--spec .glow-card__row-meta { margin-left: 0; }
.glow-card__row-title { flex: 1 1 auto; min-width: 0; }
.glow-card__rows--spec .glow-card__row-title { flex: 0 0 10rem; }
.glow-card__row-arrow { margin-left: auto; color: var(--glow-burgundy); }

/* A note under the row title. The instruction is the load-bearing half of a
   skin plan — "only at night, 1–2 times a week to start" is the difference
   between using an acid correctly and stripping a barrier with it — so it is a
   block under the product, not a parenthetical beside it. */
.glow-card__row-note {
  display: block;
  margin-top: 0.25rem;
  font-weight: 400;
  font-size: var(--glow-meta);
  line-height: 1.5;
  color: var(--glow-muted);
}

/* ---- Empty states --------------------------------------------------------
   Draw the card. Icon at low opacity as furniture, never as subject. Then say
   what is true, and — the part that matters — WHO will put something here and
   WHEN. Never "no data". */

.glow-empty {
  background: var(--glow-white);
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius);
  padding: var(--glow-s4) var(--glow-s3);
  text-align: center;
}

/* display:block is load-bearing, not tidiness. An <svg> is inline by default,
   and `margin: 0 auto` does nothing to an inline box — the mark sits on the
   text baseline instead of centring above the title. */
.glow-empty__icon {
  display: block;
  width: 1.5rem;
  height: 1.5rem;
  margin: 0 auto var(--glow-s2);
  color: var(--glow-muted);
  opacity: 0.35;
}

.glow-empty__title {
  font-size: var(--glow-small);
  font-weight: 500;
  color: var(--glow-ink);
  margin: 0 0 0.35rem;
  max-width: none;
}

.glow-empty__body {
  font-size: var(--glow-small);
  color: var(--glow-muted);
  margin: 0 auto;
  max-width: 42ch;
}

/* ---- Status ------------------------------------------------------------
   Words, not numbers, wherever a human is in the loop. Worst case is amber;
   nothing here is ever red. */

/* DELIBERATELY NOT the eyebrow (8 Aug): a bordered chip is a different grammar
   from a floating label — inside a pill, 600/0.11em reads shouty. Documented so
   the next normalisation pass does not "fix" it. */
.glow-status {
  display: inline-block;
  font-size: var(--glow-tag);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-radius: var(--glow-radius-sm);
  padding: 0.3rem 0.7rem;
  border: 1px solid transparent;
}

.glow-status--ready   { background: var(--glow-blush); color: var(--glow-burgundy); border-color: #E6D2CE; }
.glow-status--waiting { background: #F6EFE4; color: #8A5A2B; border-color: #EADDC6; }
.glow-status--quiet   { background: var(--glow-light); color: var(--glow-muted); border-color: var(--glow-border); }

/* A location, never a score. "Module 6 of 12" — count in ink, any qualifier
   muted. No bars, no percentages, no zero states rendered as 0%. */
.glow-position { font-size: var(--glow-small); color: var(--glow-muted); }
.glow-position strong { font-weight: 500; color: var(--glow-ink); }

.glow-note {
  font-size: var(--glow-small);
  color: var(--glow-muted);
  border-left: 2px solid var(--glow-border);
  padding-left: var(--glow-s3);
  margin: var(--glow-s3) 0 0;
}

/* ── THE LINK ROW. ───────────────────────────────────────────────────────────
   Added 5 Aug 2026. A standalone `.glow-link` under a card or a block appeared
   NINE times across six templates, every one of them as
   `<p style="margin-top: var(--glow-s3)">` — and three of the nine had drifted
   to s2 and s5, so the same affordance sat at three different distances
   depending on which page you were on.

   Nobody chose that either. There was no class, so each author wrote the gap
   inline, and an inline style is invisible to the next author. A missing
   component does not stay missing; it gets re-invented once per file. */
.glow-linkrow {
  margin: var(--glow-s3) 0 0;
}

/* A quiet dated fact under a position — "Started June 2026". Was three type
   declarations written inline in portal-course.html, which is how a template
   ends up owning the design. */
.glow-startedon {
  margin: var(--glow-s2) 0 0;
  font-size: var(--glow-small);
  color: var(--glow-muted);
}


/* ==========================================================================
   ARCHETYPE PANEL — the one dramatic surface in the portal
   ========================================================================== */

/* Two columns: a mark rail, then everything else. Single column stacks it.
   The split is a CONTAINER query, not a media query, because this panel renders
   at ~960px in the portal and at whatever the QA grid gives it — a viewport
   query splits a 500px card as if it were full width and the result is cramped.

   Composition is borrowed from the Skin Revision Skin Code section, which is
   the same content on the same dark field: mark left, text right, one measure.
   Numbers from src/components/SkinCodeSection.tsx — a 2-col grid with a wide
   gap, body line-height ~1.9, and a coloured rather than grey eyebrow. */
.glow-archetype {
  container-type: inline-size;
  /* Ambient wash in the archetype's own colour, top-right, well clear of the
     text column. Flat --glow-deep read as dead space at this size; this gives
     the six cards distinct temperature for free, and gives the chip colour a
     reason to exist beyond the chip. Kept at 16% so cream text stays legible. */
  background:
    radial-gradient(ellipse at 78% 12%,
      color-mix(in srgb, var(--glow-arch-color, var(--glow-burgundy)) 16%, transparent) 0%,
      transparent 62%),
    var(--glow-deep);
  border-radius: var(--glow-radius);
  padding: clamp(1.75rem, 4vw, 2.75rem);
  color: rgba(250, 247, 242, 0.72);
}

/* The grid lives on __inner, not on .glow-archetype, because a container query
   cannot style its own container — only its descendants. Putting both the
   container-type and the queried rule on the same element silently does
   nothing, which is exactly how it failed the first time. */
.glow-archetype__inner {
  display: grid;
  align-items: start;
  gap: var(--glow-s4);
}

/* The text column is capped, not the individual paragraphs. Capping each
   element separately (40ch tagline, 52ch definition, 56ch dual) gave three
   different right edges stacked on each other and left the column under-filled.
   One cap, in one place, and every block shares an edge.

   The column is 1fr, not a fixed max, so the hairlines run to the card's full
   inset rather than stopping short of it. The MEASURE is therefore set by the
   card's own max-width — 50rem gives a ~62ch column, which is the target. Any
   layout using this panel should cap the card near 50rem for the same reason. */
@container (min-width: 34rem) {
  .glow-archetype__inner {
    grid-template-columns: 7rem minmax(0, 1fr);
    gap: var(--glow-s5);
  }
}

/* The archetype's own colour arrives as an inline custom property so the value
   stays in the data and the rule stays here. Fallback keeps a missing hex from
   rendering transparent. */
.glow-archetype__chip {
  width: 7rem;
  height: 7rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--glow-radius);
  background: var(--glow-arch-color, var(--glow-burgundy));
  box-shadow: inset 0 0 0 1px rgba(36, 28, 26, 0.12);   /* keeps the pale chips visible */
}

/* Height-constrained only. Two of the six symbols are 100x120 and four are
   100x100 — constrain height, let width follow, and neither distorts. */
.glow-archetype__symbol {
  height: 3.5rem;
  width: auto;
  color: var(--glow-cream);
}

/* Rose, not grey. The Skin Revision section uses rgba(200,140,150,0.85) for the
   same eyebrow; a neutral grey here made the whole card read monochrome brown. */
.glow-archetype__code {
  font-family: var(--glow-font-body);
  font-size: var(--glow-tag);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(214, 158, 166, 0.85);
  margin: 0 0 var(--glow-s2);
  max-width: none;
}

.glow-archetype__name {
  font-family: var(--glow-font-display);
  /* cqi, not vw. This sits in a 768px card but was clamped against the
     VIEWPORT, so at 1440 it resolved to 48px — larger than anything in the
     Sigma portal, whose biggest type is 30-32px — while the card it lives in
     never changed width. Same class of mistake as querying the wrong box.
     The panel already establishes a container, so size off that. */
  font-size: clamp(1.75rem, 5cqi, 2.25rem);
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--glow-cream);
  margin: 0 0 var(--glow-s2);
}

.glow-archetype__tagline {
  /* 8 Aug: was 20px serif. The NAME above stays serif as artwork — it is the
     identity panel, exempt like the wordmark — but the tagline is a sentence,
     and sentences are Inter. */
  font-style: italic;
  font-size: var(--glow-pt-prose);
  line-height: 1.4;
  color: rgba(250, 247, 242, 0.8);
  margin: 0 0 var(--glow-s3);
  max-width: none;   /* the column is the measure — see __inner */
}

/* One measure for the prose, and the generous leading the reference uses —
   1.9 there, 1.85 here because this sits a step smaller. */
.glow-archetype__definition {
  font-size: var(--glow-small);
  line-height: 1.85;
  color: rgba(250, 247, 242, 0.72);
  max-width: none;   /* the column is the measure — see __inner */
  margin: 0;
}

/* Secondary archetype. Rendered only when a secondary exists AND differs from
   the primary — live data contains at least one record where they are equal.

   ORDER MATTERS: this block sits AFTER the definition, never before it. It was
   between the tagline and the definition, which meant the card explained the
   SECONDARY archetype before it had explained the primary one, and dropped two
   hairlines through the optical centre. Define the thing, then qualify it. */
.glow-archetype__dual {
  margin-top: var(--glow-s4);
  padding-top: var(--glow-s3);
  border-top: 1px solid rgba(250, 247, 242, 0.14);
}

.glow-archetype__dual-label {
  font-size: var(--glow-small);
  color: rgba(250, 247, 242, 0.6);
  margin: 0 0 0.35rem;
  max-width: none;
}

.glow-archetype__dual-label strong { font-weight: 500; color: rgba(250, 247, 242, 0.9); }

.glow-archetype__dual-body {
  /* --glow-meta, not an ad-hoc 13px. The portal had 11/13/14 all doing
     similar work within a 3px band, which the eye reads as inconsistency
     rather than hierarchy. 12px is far enough from 14 to be legible as a
     step. */
  font-size: var(--glow-meta);
  line-height: 1.55;
  color: rgba(250, 247, 242, 0.5);
  margin: 0;
  max-width: none;   /* the column is the measure — see __inner */
}

/* The rule above the actions is unconditional. Previously the dual block was
   the only thing drawing rules, so a card with a secondary and a card without
   read as two different components. Now the grammar is constant: everything
   below the definition is separated by a hairline, secondary or not. */
.glow-archetype__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--glow-s3) var(--glow-s4);
  margin-top: var(--glow-s4);
  padding-top: var(--glow-s3);
  border-top: 1px solid rgba(250, 247, 242, 0.14);
}

.glow-archetype__actions .glow-link { color: rgba(250, 247, 242, 0.75); }
.glow-archetype__actions .glow-link:hover { border-color: rgba(250, 247, 242, 0.5); }

/* Quiet, but legible — 0.55 alpha on the deep field, not the 0.4 the timestamp
   uses. A qualifier nobody can read is decoration. */
.glow-archetype__caveat {
  font-size: var(--glow-small);
  line-height: 1.6;
  color: rgba(250, 247, 242, 0.55);
  border-top: 1px solid rgba(250, 247, 242, 0.14);
  padding-top: var(--glow-s3);
  margin: var(--glow-s4) 0 0;
  max-width: 52ch;
}

.glow-archetype__meta {
  font-size: var(--glow-meta);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(250, 247, 242, 0.4);
  margin: var(--glow-s4) 0 0;
  max-width: none;
}

/* The caveat already draws the rule; a second gap under it doubles the space. */
.glow-archetype__caveat + .glow-archetype__meta { margin-top: var(--glow-s2); }


/* ==========================================================================
   QA harness — templates/portal-archetype-qa.html only
   ========================================================================== */

/* Single column, capped at the portal's content width, so each card renders at
   the size it will actually be. The two-column grid was the harness lying: it
   showed the panel at ~500px, which is a width it never has in the portal, and
   the unequal card heights left voids that read as a design fault when they
   were a harness fault. One column, real width, honest review. */
.glow-qa { display: grid; gap: var(--glow-s5); max-width: 50rem; }

.glow-qa__label {
  font-family: var(--glow-font-body);
  font-size: var(--glow-tag);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--glow-muted);
  margin: 0 0 0.5rem;
}

/* ---- DUTCH display harness — portal-results-qa.html ---------------------
   These primitives are on trial, not adopted. If an option wins, its rules
   move up into the CARDS section and the rest are deleted rather than left
   lying around for someone to reach for by accident. */

.glow-qa__inner { padding: var(--glow-s5) var(--glow-gutter); max-width: 58rem; margin: 0 auto; }
.glow-qa__h1 { font-family: var(--glow-font-display); font-size: 2.25rem; line-height: 1.1; margin: 0 0 var(--glow-s2); }
.glow-qa__block { margin-top: var(--glow-s6); }
.glow-qa__note { font-size: var(--glow-small); line-height: 1.7; color: var(--glow-ink-soft); max-width: 62ch; margin: 0 0 var(--glow-s3); }
.glow-qa__row { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: var(--glow-s3); }
.glow-qa__card { text-align: center; }
.glow-qa__val { font-family: var(--glow-font-display); font-size: 1.75rem; margin: var(--glow-s1) 0 0; }
.glow-qa__val span { font-family: var(--glow-font-body); font-size: var(--glow-meta); color: var(--glow-muted); margin-left: 0.3rem; }
.glow-qa__metric { font-size: var(--glow-small); color: var(--glow-muted); margin: 0.15rem 0 0; }

.glow-tag-pill {
  display: inline-block;
  font-size: var(--glow-tag);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  background: var(--glow-blush);
  color: var(--glow-burgundy);
  border-radius: var(--glow-radius-sm);
  padding: 0.35rem 0.85rem;
  margin: 0 0 var(--glow-s3);
}

/* .glow-dial's sizing lives with the rest of the dial further down (20rem).
   A second, earlier `max-width: 150px` declaration sat here and was dead —
   silently overridden by source order. Removed 30 Aug 2026. Exactly the drift
   B-08 exists to stop: two rules for one component, neither aware of the
   other, and the loser invisible until someone reorders the file. */

/* ---- THE ANALYTE ROW — adopted 3 Aug 2026 ------------------------------
   One reference range, drawn and LABELLED, with the client's result on it.

   Three things the first pass got wrong, all of which made it read as a mock:

   1. THE BAND WAS INVISIBLE. --glow-blush on --glow-light is a ~4% luminance
      step; at 6px tall the reference range — the single most important mark on
      the row — could not be seen. It now carries its own tint and a hairline.
   2. THE LIMITS WERE NOT PRINTED. The lab's own dial prints 1.8 and 4.5. A band
      with no numbers tells a client where she sits without telling her what she
      sits inside, which is most of the information gone.
   3. EVERY ROW READ AT ONE WEIGHT. Out-of-range now moves the marker and the
      status word to burgundy, and draws the distance from the band edge to the
      marker. The DISTANCE is the information — not a red/green grade.

   Still no traffic lights: burgundy marks "outside the expected range", which is
   a fact, not "bad", which would be a verdict the clinic has not given. */

/* THE ROW, RETUNED 3 Aug. Hemi: "it somehow feels muddy and the rows tend to
   blend into each other and you think 'what does this even say and what do I do
   with it?'"

   Diagnosis, and it was not subtle once counted: FIVE type sizes per row —
   15px name, 13px plain, 12px status, 17px value, 11px unit — with three of
   them in near-identical greys (#241C1A, #4A3E39, #7A6A62), all left-aligned and
   stacked with 2px between them. Three lines of similar grey text at similar
   sizes is one grey blob, not a hierarchy.

   Worse: THE STATUS WORD WAS THE FAINTEST THING ON THE ROW. It is the answer to
   "what do I do with it", and it was third in the stack in the lightest colour
   on the page.

   Three changes:
     1. Two lines left (name, plain), never three. The status moves right, beside
        the value, where the eye lands after crossing the bar.
     2. The greys separate. Name goes to full ink at 500; plain drops to a
        genuinely lighter tone so it reads as subordinate rather than competing.
     3. A fixed row rhythm, so a two-line plain does not make one row 20px taller
        than its neighbours and turn the column ragged. */

.glow-metric {
  display: grid;
  grid-template-columns: minmax(0, 15rem) minmax(6rem, 1fr) minmax(8rem, auto);
  align-items: center;
  gap: var(--glow-s3);
  min-height: 4.75rem;
  padding: var(--glow-s2) var(--glow-s3);
}
/* One step darker than --glow-light. At 50-odd rows the lighter rule stopped
   separating anything and the table read as a single field of text. */
.glow-metric + .glow-metric { border-top: 1px solid var(--glow-border); }

.glow-metric__id { min-width: 0; }
.glow-metric__name {
  display: block; font-size: var(--glow-pt-row); font-weight: 500;
  line-height: 1.3; color: var(--glow-ink);
}
.glow-metric__plain {
  display: block; font-size: 0.8125rem; line-height: 1.4;
  color: rgba(36, 28, 26, 0.5); margin-top: 0.2rem;
}

.glow-metric__v { text-align: right; white-space: nowrap; }
.glow-metric__v b {
  font-weight: 500; font-size: var(--glow-pt-value); letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums; color: var(--glow-ink);
}
.glow-metric__v > span { font-size: var(--glow-tag); color: var(--glow-muted); margin-left: 0.3rem; }
.glow-metric__status {
  display: block; font-style: normal; font-size: var(--glow-meta);
  line-height: 1.3; color: var(--glow-muted); margin-top: 0.3rem;
  white-space: normal;
}
.glow-metric--out .glow-metric__status { color: var(--glow-flag); font-weight: 500; }

.glow-bar { position: relative; height: 34px; }
.glow-bar__rail { position: absolute; left: 0; right: 0; top: 15px; height: 1px; background: var(--glow-border); }
.glow-bar__band {
  position: absolute; top: 9px; height: 13px; border-radius: 7px;
  background: rgba(109, 41, 50, 0.13);
  border: 1px solid rgba(109, 41, 50, 0.30);
}
.glow-bar__cap { position: absolute; top: 6px; width: 1px; height: 19px; background: rgba(109, 41, 50, 0.55); }
/* Drawn ONLY when the value falls outside the band. */
.glow-bar__gap { position: absolute; top: 15px; height: 1px; background: var(--glow-flag); }
.glow-bar__mark {
  position: absolute; top: 8.5px; width: 15px; height: 15px; margin-left: -7.5px;
  border-radius: 50%; background: var(--glow-ink);
  box-shadow: 0 0 0 3px var(--glow-white), 0 1px 4px rgba(36, 28, 26, 0.30);
}
.glow-metric--out .glow-bar__mark { background: var(--glow-flag); }
/* A hairline down the left edge of a flagged row. At a glance down 25 rows this
   is what finds the five that matter — the marker alone is too small a target. */
.glow-metric--out { box-shadow: inset 3px 0 0 var(--glow-flag); background: rgba(168,100,30,0.028); }
/* ---- THE SPAN ROW — a period on the results-row geometry -----------------
   Added 30 Aug 2026. Shares .glow-metric's grid and .glow-bar's parts with the
   results rows, so a script, a plan and a lab marker can never drift into three
   different-looking things again. B-08 §9.

   ⚠ COLOUR MARKS CATEGORY, NEVER URGENCY. The tones below say WHAT a row is —
   a script, a plan — not how worried to be about it. Nothing here may shade
   toward red as an endpoint nears: CLAUDE.md bars loss framing, and this
   component's own copy calls it "a fact about the paperwork, not a countdown".
   If a future edit makes the bar redden with time, that edit is the defect. */

/* The elapsed portion. Sits UNDER the marker, above the band. */
.glow-bar__elapsed {
  position: absolute; left: 0; top: 9px; height: 13px;
  border-radius: 7px 0 0 7px;
  background: rgba(109, 41, 50, 0.28);
}

/* An upcoming appointment. A neutral notch — it shows something is coming,
   it does not grade the gap between them. */
.glow-bar__tick {
  position: absolute; top: 4px; width: 2px; height: 23px; margin-left: -1px;
  border-radius: 1px; background: var(--glow-action); opacity: 0.55;
}

/* Endpoint labels on a span are dates, not numbers, so they need room and must
   not collide at the two ends. */
.glow-bar--span .glow-bar__lim { font-variant-numeric: normal; }
.glow-bar--span .glow-bar__lim[style*="left:0%"]   { transform: none; }
.glow-bar--span .glow-bar__lim[style*="left:100%"] { transform: translateX(-100%); }

/* Category tones. Sage for the plan so the two rows are separable at a glance;
   the script keeps the default burgundy. */
.glow-metric--plan .glow-bar__elapsed { background: rgba(65, 73, 62, 0.28); }
.glow-metric--plan .glow-bar__tick,
.glow-metric--plan .glow-bar__mark    { background: var(--glow-sage); }

.glow-bar__lim {
  position: absolute; top: 25px; transform: translateX(-50%);
  font-size: var(--glow-tag); font-variant-numeric: tabular-nums;
  color: rgba(36, 28, 26, 0.45); white-space: nowrap;
}

/* No verified cycle context → no band. The marker still shows the value; what is
   withheld is the comparison, which is the part that would be wrong. */
.glow-bar--nocontext .glow-bar__rail {
  background: repeating-linear-gradient(90deg, var(--glow-border) 0 4px, transparent 4px 8px);
}

.glow-key { display: flex; flex-wrap: wrap; gap: var(--glow-s3); font-size: var(--glow-meta); color: var(--glow-muted); margin: 0 0 var(--glow-s2); }
.glow-key span { display: inline-flex; align-items: center; gap: 0.5rem; }
.glow-key i { width: 26px; height: 11px; border-radius: 6px; background: rgba(109,41,50,.10); border: 1px solid rgba(109,41,50,.22); }
.glow-key u { width: 11px; height: 11px; border-radius: 50%; background: var(--glow-ink); box-shadow: 0 0 0 2px var(--glow-white); }

/* ---- The "?" explainer -------------------------------------------------
   Glow cannot diagnose. It CAN say what a marker is and why it bears on skin,
   which is the difference between a client understanding her result and
   googling it at 11pm. <details> so it works with no JS at all. */
.glow-hx { margin-left: auto; position: relative; }
.glow-hx summary {
  list-style: none; cursor: pointer;
  width: 22px; height: 22px; border-radius: 50%;
  border: 1px solid var(--glow-border);
  display: grid; place-items: center;
  font-size: var(--glow-meta); font-weight: 500; color: var(--glow-muted);
  transition: all var(--glow-dur) var(--glow-ease);
}
.glow-hx summary::-webkit-details-marker { display: none; }
.glow-hx summary:hover { border-color: var(--glow-ink); color: var(--glow-ink); }
.glow-hx[open] summary { background: var(--glow-ink); border-color: var(--glow-ink); color: var(--glow-cream); }
.glow-hx__p {
  position: absolute; right: 0; top: 32px; width: min(29rem, 80vw);
  background: var(--glow-white); border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius); padding: var(--glow-s2) var(--glow-s3);
  box-shadow: 0 12px 40px rgba(36, 28, 26, 0.13);
  z-index: 10; font-size: var(--glow-small); line-height: 1.68; color: var(--glow-ink-soft);
}
.glow-hx__p p { margin: 0 0 0.75rem; } .glow-hx__p p:last-child { margin: 0; }
.glow-hx__p b { color: var(--glow-ink); font-weight: 500; }

@container (max-width: 46rem) {
  .glow-metric { grid-template-columns: 1fr auto; gap: 0.5rem var(--glow-s2); }
  .glow-bar { grid-column: 1 / -1; }
}

.glow-curve { width: 100%; height: auto; display: block; }
.glow-curve__axis {
  display: flex; justify-content: space-between;
  font-size: var(--glow-meta); color: var(--glow-muted); margin-top: 0.4rem;
}

@media (max-width: 600px) {
  .glow-qa__row { grid-template-columns: 1fr; }
  .glow-qa__metric-row { flex-wrap: wrap; }
  .glow-qa__metric-name { flex: 1 1 100%; }
  .glow-qa__metric-val { flex: 0 0 auto; }
}


/* ==========================================================================
   RESPONSIVE
   Below 940px the rail leaves the flow and becomes a drawer holding the same
   markup, so there is one nav to maintain rather than two.
   ========================================================================== */

@media (max-width: 939px) {
  .glow-portal { height: auto; min-height: 100vh; overflow: visible; display: block; }

  .glow-rail {
    position: fixed;
    inset: 0 auto 0 0;
    z-index: 120;
    width: 17rem;
    transform: translateX(-100%);
    transition: transform var(--glow-dur) var(--glow-ease);
  }

  .glow-portal[data-drawer="open"] .glow-rail { transform: none; }

  .glow-portal__scrim {
    position: fixed;
    inset: 0;
    z-index: 110;
    background: rgba(36, 28, 26, 0.45);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--glow-dur) var(--glow-ease);
  }

  .glow-portal[data-drawer="open"] .glow-portal__scrim { opacity: 1; pointer-events: auto; }

  .glow-portal__content { overflow: visible; }

  /* No collapse control in a drawer — it has nothing to collapse into. And with
     the button gone, the credit's divider would be separating it from nothing,
     so that goes too. A rule with no content under it reads as a broken layout. */
  .glow-rail__toggle { display: none; }
  .glow-rail__credit { border-bottom: 0; margin-bottom: 0; }
}

@media (min-width: 940px) {
  .glow-burger--portal,
  .glow-portal__scrim { display: none; }
}


/* ==========================================================================
   RESULT DETAIL — the DUTCH render, inside the portal shell
   ==========================================================================

   Adopted 3 Aug 2026. Hemi: *"I want the aesthetic, balance and styling"* of
   the MPS portal, explicitly NOT its gamification — so this borrows MPS's
   generosity of explanation and its entry points, and none of its 0% rings,
   progress bars or step counters. A clinical record that scores you is a record
   you perform for.

   The page is deliberately the most complex thing in the portal, and it is being
   built early rather than last: Hemi's reasoning is that the bar set here is the
   bar for everything after it. */

/* The .glow-interp* component that lived here (the RN's written
   interpretation panel) was DELETED 8 Aug 2026 — dead CSS. The 3 Aug ruling
   removed the RN interpretation from the results page (RN Labs scope: skin,
   not endocrinology) and no template has referenced these classes since. It
   carried an 18px radius, a 12px radius and three literal font sizes the rest
   of the file had already given up. */

/* ---- Orientation — three lines before any number ------------------------ */
.glow-orient { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: var(--glow-s3) var(--glow-s4); }
.glow-orient > div { border-top: 1px solid var(--glow-border); padding-top: 0.9rem; }
.glow-orient strong { display: block; font-weight: 500; color: var(--glow-ink); margin-bottom: 0.3rem; font-size: var(--glow-pt-row); }
.glow-orient p { margin: 0; font-size: var(--glow-small); line-height: 1.6; color: var(--glow-ink-soft); max-width: none; }

/* A section states what it covers BEFORE the numbers start. */
/* Section lead. Same treatment as card prose — they are the same job and were
   drifting apart at 1.65/68ch. 30 Aug 2026. */
.glow-slead { font-size: var(--glow-pt-prose); line-height: 1.55; color: var(--glow-text-soft); max-width: 58ch; margin: 0 0 var(--glow-s3); }

/* ---- What happens now ---------------------------------------------------
   A result page that ends at the last number leaves the client holding
   something with nowhere to take it. */
/* Sits in the same vertical rhythm as .glow-block, which separates at --glow-s4.
   It is deliberately headerless — an invitation is not a record module — but it
   must not read as an orphan glued to the block above it. 30 Aug 2026. */
.glow-next { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--glow-s2); margin-top: var(--glow-s4); }
/* One rule for the live card and the pending one — .glow-next__pending used to
   be a byte-for-byte copy of these declarations 450 lines away, which is how
   the two drift apart. The variants differ only where they mean to: border
   style, and the colour of the action line. */
.glow-next a,
.glow-next__pending {
  display: block; background: var(--glow-white);
  border: 1px solid var(--glow-border); border-radius: var(--glow-radius);
  padding: var(--glow-s3);
  transition: border-color var(--glow-dur) var(--glow-ease);
}
.glow-next__pending { border-style: dashed; }
.glow-next a:hover { border-color: var(--glow-muted); }
.glow-next strong,
.glow-next__pending strong { display: block; font-size: var(--glow-pt-row); font-weight: 500; color: var(--glow-ink); margin-bottom: 0.3rem; }
.glow-next span,
.glow-next__pending span { display: block; font-size: var(--glow-small); line-height: 1.55; color: var(--glow-muted); }
.glow-next em,
.glow-next__pending em { display: block; font-style: normal; font-size: var(--glow-small); color: var(--glow-burgundy); margin-top: 0.7rem; }
.glow-next__pending em { color: var(--glow-muted); }

/* ---- The diurnal chart --------------------------------------------------
   Each of the four timed points has its OWN expected range, drawn as the block
   behind it. Plotting four differently-ranged values on one bare axis makes a
   mid-range bedtime reading look like a collapse next to a mid-range morning
   one — the blocks are what stop that. */
.glow-chart { width: 100%; height: auto; display: block; overflow: visible; }
.glow-chart .cg { stroke: var(--glow-light); stroke-width: 1; }
.glow-chart .cay { font: 400 10px var(--glow-font-body); fill: var(--glow-muted); }
.glow-chart .cbd { fill: rgba(109, 41, 50, 0.09); stroke: rgba(109, 41, 50, 0.20); }
.glow-chart .cbr { font: 400 10px var(--glow-font-body); fill: var(--glow-muted); }
.glow-chart .cdh { fill: var(--glow-white); }
.glow-chart .cdt { fill: var(--glow-burgundy); }
.glow-chart .cvv { font: 500 13px var(--glow-font-body); fill: var(--glow-ink); }
.glow-chart .cxx { font: 500 12px var(--glow-font-body); fill: var(--glow-ink-soft); }

/* ---- Document header strip ---------------------------------------------- */
.glow-dochdr {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--glow-s3) var(--glow-s4);
  border-top: 1px solid var(--glow-ink);
  padding-top: 1.1rem;
}
.glow-dochdr .k { display: block; margin-bottom: 0.3rem; }
.glow-dochdr .v { display: block; font-size: var(--glow-pt-row); color: var(--glow-ink); }

/* ---- The assumed-range caution ------------------------------------------ */
.glow-caution {
  /* Frame moved onto the --glow-flag* attention tokens, 8 Aug — same hue
     family the analyte flags use, so "look here" is one colour system. The
     dark text browns below stay literal: they are the readable ramp of this
     hue and two more tokens for one component is over-tokenising. */
  background: var(--glow-flag-soft); border: 1px solid var(--glow-flag-line); border-left: 3px solid var(--glow-flag);
  border-radius: var(--glow-radius); padding: var(--glow-s2) var(--glow-s3);
}
.glow-caution h2,
.glow-caution__t { font-size: var(--glow-pt-title); font-weight: 600; letter-spacing: -0.01em; color: #7A4E1D; margin: 0 0 0.5rem; }
.glow-caution p { color: #5E4426; margin: 0 0 0.75rem; font-size: var(--glow-pt-prose); line-height: 1.55; max-width: 58ch; }
.glow-caution p:last-child { margin: 0; }


/* ---- Collapsible sections ------------------------------------------------
   Sixty analytes is not a page anyone reads top to bottom. Sections that hold
   nothing outside the expected range open COLLAPSED, with the summary on the
   header — "12 markers, all within range" is a complete answer for most people
   most of the time, and the detail is one click away for the rest.

   This is navigation, not scoring. It surfaces where to look using a fact the
   laboratory has already stated on its own report; it does not rank the client,
   grade her, or derive anything the lab did not print. */
.glow-sec { border: 1px solid var(--glow-border); border-radius: var(--glow-radius); background: var(--glow-white); overflow: hidden; }
.glow-sec + .glow-sec { margin-top: var(--glow-s2); }
.glow-sec > summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: baseline; gap: var(--glow-s2);
  padding: var(--glow-head-y) var(--glow-s3);
  transition: background var(--glow-dur) var(--glow-ease);
}
.glow-sec > summary::-webkit-details-marker { display: none; }
.glow-sec > summary:hover { background: var(--glow-cream); }
.glow-sec[open] > summary { border-bottom: 1px solid var(--glow-border); }
.glow-sec__c { font-size: var(--glow-small); color: var(--glow-muted); }
.glow-sec__c b { font-weight: 500; color: var(--glow-flag); }
/* The chevron rotates; it is the only motion on the page. */
.glow-sec__chev { margin-left: auto; width: 16px; height: 16px; color: var(--glow-muted); transition: transform var(--glow-dur) var(--glow-ease); flex: none; align-self: center; }
.glow-sec[open] .glow-sec__chev { transform: rotate(180deg); }
.glow-sec__lead { font-size: var(--glow-small); line-height: 1.7; color: var(--glow-ink-soft); max-width: 62ch; margin: 0; padding: var(--glow-s3) var(--glow-s3) 0; }
.glow-sec__grp { padding-top: var(--glow-s3); }
/* TIER 2 — names a group inside a section. Stays one step lighter than the
   block label so the nesting is legible without reading the words. */
.glow-sec__grp-t { margin: 0; padding: 0 var(--glow-s3) 0.75rem; }
.glow-sec__grp + .glow-sec__grp { border-top: 1px solid var(--glow-light); }
/* A hidden group still matches the sibling selector above, which hands the
   next group a divider with nothing before it — on the portal shop, a stray
   hairline above the first brand whenever "Your favourites" is empty. */
.glow-sec__grp[hidden] + .glow-sec__grp { border-top: 0; }

/* ---- The percentile slider ----------------------------------------------
   A DIFFERENT INSTRUMENT to the analyte bar, because it answers a different
   question. The bar asks "where does this value sit in its range"; the slider
   asks "how does this RATIO compare with everyone else's". Drawing them the
   same way would imply the two are comparable, and they are not — one is a
   measurement, the other is a population position.

   The lab prints these as "higher than 82% of the population". That phrasing is
   kept because rewording a percentile is how it becomes a grade. */
.glow-slider { padding: var(--glow-s2) var(--glow-s3); }
.glow-slider + .glow-slider { border-top: 1px solid var(--glow-light); }
.glow-slider__h { display: flex; align-items: baseline; justify-content: space-between; gap: var(--glow-s2); margin-bottom: 0.15rem; }
.glow-slider__n { font-size: var(--glow-pt-row); color: var(--glow-ink); }
.glow-slider__v { font-size: var(--glow-pt-row); font-weight: 500; font-variant-numeric: tabular-nums; color: var(--glow-ink); }
.glow-slider__p { font-size: var(--glow-meta); color: var(--glow-ink-soft); margin: 0 0 0.8rem; line-height: 1.5; max-width: 60ch; }
.glow-slider__track {
  position: relative; height: 12px; border-radius: 6px;
  background: var(--glow-light); margin-bottom: 2.5rem;
}
/* The optimal band the lab shades on its own slider. */
.glow-slider__band { position: absolute; inset: 0 auto; height: 12px; border-radius: 6px; background: rgba(109,41,50,0.12); }
.glow-slider__mark {
  position: absolute; top: -3px; width: 2px; height: 18px;
  background: var(--glow-ink); border-radius: 1px; transform: translateX(-1px);
}
.glow-slider--out .glow-slider__mark { background: var(--glow-flag); }
.glow-slider__cap {
  position: absolute; top: 20px; transform: translateX(-50%);
  font-size: var(--glow-tag); font-variant-numeric: tabular-nums; color: var(--glow-muted); white-space: nowrap;
}
/* The ends sit BELOW the percentage caption, not on its line. The negative
   margin here pulled them up onto the same row, so a marker near either end put
   "82%" straight through "2-OH-E1" — invisible on a wide screen where the two
   are 500px apart, and unreadable at 390 where they are not. */
.glow-slider__ends {
  display: flex; justify-content: space-between; gap: var(--glow-s2);
  font-size: var(--glow-tag); color: var(--glow-muted); margin-top: -0.45rem;
}

/* ---- What you told us ---------------------------------------------------
   The self-reported comments from the requisition. They belong near the top
   because every range on the page is read against them — "reports regular
   cycles" is what justifies the luteal column. */
.glow-said { display: grid; gap: 0.65rem; }
.glow-said li {
  list-style: none; position: relative; padding-left: 1.4rem;
  font-size: var(--glow-pt-prose); line-height: 1.6; color: var(--glow-ink-soft);
}
.glow-said li::before {
  content: ""; position: absolute; left: 0; top: 0.6em;
  width: 6px; height: 6px; border-radius: 50%; background: var(--glow-burgundy); opacity: 0.45;
}

/* Two columns where the content is genuinely paired — cortisol beside
   cortisone, which is the comparison the lab itself invites. Balance rather
   than full-bleed everything, which was the note on the first pass. */
@container (min-width: 52rem) {
  .glow-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--glow-s2); }
}


/* A section already draws the frame. Anything nested inside it must not draw a
   second one — the diurnal chart and the lab-note block both arrive as
   .glow-card and were rendering as cards-inside-a-card, which reads as a
   rendering fault rather than as structure. */
.glow-sec .glow-card { border: 0; border-radius: 0; background: transparent; }
.glow-sec .glow-card__head { padding-inline: var(--glow-s3); }


/* The lab's commentary block, now at the TOP of its section — you read the
   finding, then the evidence. Same order the page already uses for the RN's
   note, and it saves scrolling 25 rows to be told what they meant.

   Tinted, so the lab's voice is visibly not Glow's voice. Three voices on this
   page and the reader should never have to work out which one is speaking. */
.glow-labnote {
  background: var(--glow-blush);
  border-bottom: 1px solid var(--glow-border);
  padding: var(--glow-s2) var(--glow-s3);
}
/* Attribution, not hierarchy — burgundy is doing the work of saying "this is
   someone else speaking", so it does not also need size. */
.glow-labnote__t { color: var(--glow-burgundy); margin: 0 0 0.85rem; }
.glow-labnote .glow-note { margin-top: var(--glow-s3); border-left-color: rgba(109,41,50,0.25); }


/* ---- The steroid pathway ------------------------------------------------
   Rebuilt 3 Aug. Hemi on v1: "this looks old Windows 95 basic."

   Three things were doing that, and none of them was the idea:
     · orthogonal elbow connectors with arrowheads — the visual signature of a
       process diagram, not a biological pathway. Now cubic beziers.
     · every node an identical dashed pill, so nothing had rank
     · one colour for everything, so three distinct branches read as one tangle

   Each branch now carries its own hue, and this is where per-system colour
   EARNS its place: it is doing structural work here (which chain am I on)
   rather than decorating rows. Amber is untouched and still means only "outside
   the expected range" — none of these three is amber. */

.glow-path { width: 100%; height: auto; display: block; overflow: visible; }

.glow-path .pth path { fill: none; stroke-width: 1.5; stroke-linecap: round; }
.glow-path .br-p, .glow-path .pth .br-p { stroke: #B08A6B; }
.glow-path .br-a, .glow-path .pth .br-a { stroke: #A8556A; }
.glow-path .br-c, .glow-path .pth .br-c { stroke: #6E7F63; }
.glow-path .pth .br-x { stroke: var(--glow-border); }

.glow-path .nd rect { fill: #fff; stroke-width: 1.25; }
.glow-path .nd text {
  font: 500 13.5px var(--glow-font-body); fill: var(--glow-ink);
  text-anchor: middle; dominant-baseline: middle;
}
.glow-path .nd .br-p rect { stroke: #B08A6B; fill: #FBF4EE; }
.glow-path .nd .br-a rect { stroke: #A8556A; fill: #FBF0F1; }
.glow-path .nd .br-c rect { stroke: #6E7F63; fill: #F2F5F0; }

/* Not-measured: a ring and a label, no box, no dashes. It sits on the chain
   without competing with the markers that have a value behind them. */
.glow-path .nd--off circle { fill: #fff; stroke: var(--glow-border); stroke-width: 1.5; }
.glow-path .nd--off text {
  font: 400 12.5px var(--glow-font-body); fill: var(--glow-muted);
  text-anchor: start; dominant-baseline: middle;
}

.glow-path .nd__key text { font: 400 11.5px var(--glow-font-body); fill: var(--glow-muted); dominant-baseline: middle; }
.glow-path .nd__key .k-off { fill: #fff; stroke: var(--glow-border); stroke-width: 1.5; }
.glow-path .nd__key .k-on { fill: #FBF0F1; stroke: #A8556A; stroke-width: 1.25; }

/* FOLLOW ONE BRANCH — no JavaScript. Hovering anywhere on the diagram fades
   every branch; hovering a branch restores it. Focus-within does the same from
   the keyboard, and :hover simply never fires on touch, where the whole diagram
   stays legible at full strength. Nothing is hidden and nothing is required. */
.glow-path .br-p, .glow-path .br-a, .glow-path .br-c {
  transition: opacity var(--glow-dur) var(--glow-ease);
}
.glow-path:hover .br-p, .glow-path:hover .br-a, .glow-path:hover .br-c { opacity: 0.22; }
.glow-path .br-p:hover, .glow-path .br-a:hover, .glow-path .br-c:hover,
.glow-path:hover .pth .br-p:hover, .glow-path:hover .pth .br-a:hover { opacity: 1; }
/* A branch's connectors light with its nodes — they share the class, so hovering
   the group in either layer restores both. */
.glow-path:hover .pth path { opacity: 0.22; }
.glow-path .nd .br-p:hover ~ *, .glow-path .nd .br-a:hover ~ * { opacity: inherit; }

@media (prefers-reduced-motion: reduce) {
  .glow-path .br-p, .glow-path .br-a, .glow-path .br-c { transition: none; }
}

.glow-path { width: 100%; height: auto; display: block; overflow: visible; }




/* ---- Reviewed and released ----------------------------------------------
   What replaced the RN's written interpretation. A STATEMENT OF FACT — a nurse
   read this, and when — not a reading of the panel. Deliberately small and
   deliberately not a card: it is a provenance line, and giving it a card would
   re-create the authority claim it exists to remove.

   Hemi, 3 Aug: what the results change "belongs on the skin journey process",
   so this points there and says nothing itself. */
.glow-relby { border-left: 2px solid var(--glow-burgundy); padding: 0.15rem 0 0.15rem var(--glow-s3); }
.glow-relby__k { display: block; }
.glow-relby__v { display: block; font-size: var(--glow-pt-prose); color: var(--glow-ink); margin-top: 0.25rem; }
.glow-relby__p { font-size: var(--glow-pt-prose); line-height: 1.55; color: var(--glow-ink-soft); max-width: 58ch; margin: var(--glow-s2) 0 0; }


/* ==========================================================================
   BLOCKS — one idea, one container
   ==========================================================================

   Hemi, 3 Aug, against the MPS portal: *"things are contained cleanly here in
   bubbles or modules… Glow looks much better but still a distance off."*

   Two structural differences, and neither is decoration:

   1. THE EYEBROW WAS OUTSIDE THE CARD. Glow floated section labels on the page
      and then drew a card under them, which is an editorial magazine pattern —
      a standfirst above a plate. MPS puts the label INSIDE, as a header row with
      a rule under it, so the label and its content are visibly one object. On a
      page of fifteen sections the difference is whether it reads as a stack of
      modules or as a continuous scroll with headings in it.

   2. NOTHING NESTS TWICE. A card inside a block draws no second frame — the
      block already drew it. The same rule .glow-sec needed.

   A .glow-block is one idea: a header that names it and a body that is it. */

.glow-block {
  background: var(--glow-white);
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius);
  /* Separation must exceed the padding INSIDE the card, or the eye cannot tell
     where one module ends and the next begins. Body padding is --glow-s3 (24px);
     this was --glow-s2 (16px), so the gap between cards was smaller than the gap
     inside them and the stack read as one crowded slab. 30 Aug 2026. */
  margin-top: var(--glow-s4);
  overflow: hidden;
}
.glow-block:first-child { margin-top: 0; }
/* The first block after the page title — the title and lead sit on the ground
   as a page header, so this needs its own gap rather than butting the lead. */
.glow-block--first { margin-top: var(--glow-s5); }
/* Inside a block the strip has already been framed, so it drops the rule that
   made it a standalone header on the page. */
.glow-block .glow-dochdr { border-top: 0; padding-top: 0; }

.glow-block__head {
  display: flex; align-items: baseline; gap: var(--glow-s2);
  padding: var(--glow-head-y) var(--glow-s3);
  border-bottom: 1px solid var(--glow-border);
}
/* TIER 1 — names a whole block. It was 11px, 500, 0.16em tracking in --glow-muted,
   which is the weakest type on the page carrying the most structural weight.
   At 11px the letterforms are too small to hold that much tracking: the word
   dissolves into evenly-spaced marks.

   12px, 600, and less tracking. Bigger letters need less space between them,
   not more — the tracking was compensating for a size that was already wrong. */
.glow-block__t { color: var(--glow-ink-soft); margin: 0; }
.glow-block__note { font-size: var(--glow-meta); color: var(--glow-muted); margin-left: auto; }
/* THE CONTENT LINE. Everything inside a card sits here. B-08 §0. */
.glow-block__body { padding: var(--glow-card-pad); }
.glow-block__body > :first-child { margin-top: 0; }
.glow-block__body > :last-child { margin-bottom: 0; }

/* Nested surfaces stand down — the block is the container. */
.glow-block .glow-card { border: 0; border-radius: 0; background: transparent; }
.glow-block .glow-card__body { padding: 0; }

/* ⚠ THE HEAD HAD TO STAND DOWN TOO, and for four years of this file it did not.
   .glow-card__body was zeroed above; .glow-card__head kept its own
   `padding: var(--glow-head-y) var(--glow-s4)` and its bottom rule. Inside a
   block that produced a card title indented one step FURTHER RIGHT than its own
   body text, with a stray full-width rule under it and a dead vertical gap
   above — the exact "clumsy and hard to read" half of the portal, sitting
   beside row lists that were flush and tight and looked right.

   The tell was that nothing was misaligned in isolation: the head is correct in
   a standalone card and the body is correct in a block. Only the combination —
   which is the normal case — was wrong. */
.glow-block .glow-card__head {
  padding: 0 0 var(--glow-s2);
  border-bottom: 0;
}
.glow-block .glow-sec { border-radius: var(--glow-radius); }
.glow-block .glow-sec .glow-card__body { padding: var(--glow-s3); }

/* A block whose body IS a list of sections needs no padding of its own. */
.glow-block--flush .glow-block__body { padding: var(--glow-s3); background: var(--glow-pt-well); }


/* ---- The pathway on a phone ---------------------------------------------
   The diagram is 1000 units wide with 13.5px labels. Scaled into a 284px column
   those labels render at under 4px — present, and unreadable.

   So it keeps a legible minimum and its container scrolls. 860px holds the text
   at ~11.6px, which is the smallest it stays comfortably readable at. Above
   900px the cap lifts and it behaves exactly as before. */
.glow-path-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.glow-path-hint {
  display: none; font-size: var(--glow-meta); color: var(--glow-muted);
  margin: 0.6rem 0 0;
}
@media (max-width: 899px) {
  .glow-path { min-width: 860px; }
  .glow-path-hint { display: block; }
  /* The scroller needs the block's padding back, so the diagram can run to the
     edge of the screen instead of stopping short of it.

     Was: "⚠ MUST MATCH .glow-block__body's inset". It now REFERENCES that inset
     via --glow-card-pad, because two rules that must agree have to share a
     variable — a comment is not a constraint, and this is the same failure that
     copied the button geometry into four files. 30 Aug 2026, B-08 §0. */
  .glow-block__body > .glow-card .glow-path-scroll {
    margin-inline: calc(var(--glow-card-pad) * -1);
    padding-inline: var(--glow-card-pad);
  }
}


/* ==========================================================================
   MOBILE — four defects found on a real device, 3 Aug
   ========================================================================== */

@media (max-width: 620px) {

  /* 1. THE PATHWAY WAS MOSTLY EMPTY CANVAS.
     Holding an 860px minimum in a 412px window shows 48% of the diagram — and
     because the trunk sits at x=440 of 1000, the visible left-hand slice is the
     part with nothing in it. The client scrolls into a blank field and has to
     work out that the picture is somewhere off to the right.

     Fixing it by scrolling further was the wrong instinct. The fix is to make
     the WHOLE diagram fit the scroll, and buy back legibility by enlarging the
     type inside the SVG instead of the SVG itself. At 620px the 1000-unit
     viewBox renders at 0.62 — so 19 units lands at ~11.8px, which reads, and
     the entire chain is reachable in ~200px of scroll rather than 450. */
  .glow-path { min-width: 620px; }
  .glow-path .nd text { font-size: 19px; }
  .glow-path .nd--off text { font-size: 18px; }
  .glow-path .nd__key text { font-size: 16px; }

  /* 2. THE SECTION HEADER WRAPPED THROUGH ITSELF.
     Serif title, marker count and chevron on one flex row is fine at 992px and
     falls apart at 412 — the title breaks to two lines and the count breaks
     mid-phrase, which reads as a rendering fault rather than a long heading. */
  .glow-sec > summary { flex-wrap: wrap; align-items: center; padding-right: 3rem; position: relative; }
  /* the size override that was here is gone: .glow-sec__t rides the shared
     title token */
  .glow-sec__t { flex: 1 1 100%; }
  .glow-sec__c { flex: 1 1 100%; margin-top: 0.2rem; }
  .glow-sec__chev { position: absolute; right: var(--glow-s3); top: 1.35rem; margin: 0; }

  /* 2b. THE TILE STATUS CHIP WRAPPED INSIDE ITS OWN PILL.
     Measured 4 Aug at 390: "After your first visit" and "Runs on The Skin Code"
     both rendered 49px tall against the 30px of the short ones — a two-line
     badge, which reads as a break rather than a label.

     The chips got longer on purpose. They now name whose move it is instead of
     saying "Not started" at everything, and that is worth more than brevity, so
     the layout gives way rather than the words.

     Chip first, title under it — an eyebrow label, which is a deliberate
     pattern rather than a squeezed one. Same wrap trick as the section header
     above: order swaps them, and 100% basis on the title forces the break. */
  .glow-tile__head { flex-wrap: wrap; align-items: flex-start; gap: var(--glow-s1) var(--glow-s2); }
  .glow-tile__head .glow-status { order: 1; }
  .glow-tile__title { order: 2; flex: 1 1 100%; }

  /* 3. FIVE STACKED ROWS OF METADATA before the client reaches anything.
     Two up, and it is a quarter of the height. */
  .glow-dochdr { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--glow-s3) var(--glow-s2); }

  /* 4. THE LAB'S BULLETS GAVE UP 28px OF A 340px COLUMN to an indent, on the
     longest passages of text on the page. */
  .glow-said li { padding-left: 1rem; }
  .glow-said li::before { width: 5px; height: 5px; }

  /* The scroll hint belongs with the diagram, not adrift under the legend. */
  .glow-path-hint { margin-top: 0.35rem; font-style: italic; }

  /* 5. NESTED PADDING COMPOUNDS, and on a 412px screen it eats the page.
     block body 24 + section 0 + lab-note 32 + bullet indent 16 = 72px of left
     inset before a word appears, and the same again on the right — leaving the
     longest prose on the page in a ~200px column.

     Each container is individually reasonable; it is the STACK that is wrong,
     and it is only ever visible on a narrow screen. Every level steps down. */
  .glow-block__body,
  .glow-block--flush .glow-block__body { padding: var(--glow-s2); }
  .glow-block__head { padding-inline: var(--glow-s2); }
  .glow-labnote { padding: var(--glow-s3) var(--glow-s2); }
  .glow-sec > summary { padding-inline: var(--glow-s2); padding-right: 2.75rem; }
  .glow-sec__lead { padding-inline: var(--glow-s2); }
  .glow-sec__grp-t { padding-inline: var(--glow-s2); }
  .glow-metric { padding-inline: var(--glow-s2); }
  .glow-slider { padding-inline: var(--glow-s2); }
  .glow-sec .glow-card__body { padding: var(--glow-s2); }
  .glow-sec__chev { right: var(--glow-s2); }
  .glow-block__body > .glow-card .glow-path-scroll {
    margin-inline: calc(var(--glow-s2) * -1);
    padding-inline: var(--glow-s2);
  }

  /* 5b. THE STEP-DOWN MISSED THE FOUR COMPONENTS THE OVERVIEW IS MADE OF.
     Added 5 Aug. Everything above steps down; .glow-tile, .glow-card__body,
     .glow-card__row and .glow-empty did not — so on a 390px screen the results
     pages breathed and the overview, which is the page every client opens
     first, still carried a 32px inset on every side of every card.

     It was invisible in review because the pages that were being reviewed
     closely at the time were the DUTCH ones, and those were fixed. The overview
     was never opened at 390 after the tiles were built. */
  .glow-tile { padding: var(--glow-s2); }
  .glow-card__body { padding: var(--glow-s2); }
  .glow-card__head { padding-inline: var(--glow-s2); }
  .glow-card__row,
  .glow-card__intro { padding-inline: var(--glow-s2); }
  .glow-empty { padding: var(--glow-s4) var(--glow-s2); }
}

/* .glow-next__pending — a document that does not exist yet is not a link.
   Styled with .glow-next a in one grouped rule further up (8 Aug); it used to
   be duplicated here and the two copies had already started to drift. */


/* ---- The client strip ----------------------------------------------------
   Sits under the top bar on every portal page. A record header, not a
   dashboard: name, where they are seen, and two facts about the past.

   Deliberately absent: visit counts, rings, percentages, streaks, "next step".
   This is the single most-seen element in the portal and therefore the one
   where a scoring habit would do the most damage. */
.glow-whois {
  flex: none;
  display: flex; align-items: center; gap: var(--glow-s2);
  padding: var(--glow-s2) clamp(var(--glow-s2), 3vw, var(--glow-s4));
  background: var(--glow-white);
  border-bottom: 1px solid var(--glow-border);
}
.glow-whois__av {
  flex: none; width: 34px; height: 34px; border-radius: 50%;
  background: var(--glow-blush); color: var(--glow-burgundy);
  display: grid; place-items: center;
  /* Inter, 8 Aug — the wordmark exemption covers Glow's identity, not a
     client's initial. */
  font-size: var(--glow-pt-prose); font-weight: 500;
}
.glow-whois__id { min-width: 0; }
.glow-whois__n { display: block; font-size: var(--glow-pt-row); font-weight: 500; color: var(--glow-ink); line-height: 1.25; }
.glow-whois__m { display: block; font-size: var(--glow-meta); color: var(--glow-muted); }
.glow-whois__facts { display: flex; gap: var(--glow-s4); margin-left: auto; }
.glow-whois__facts span { display: block; font-size: var(--glow-small); color: var(--glow-ink); white-space: nowrap; }
.glow-whois__facts b { display: block; margin-bottom: 0.1rem; }
/* On a phone the facts drop rather than crush the name. They are context, and
   the name is the thing that has to survive. */
@media (max-width: 620px) { .glow-whois__facts { display: none; } }

/* The closing aside on the overview sits further from the grid above it than a
   note normally does — it is a change of subject, not a footnote to the last
   card. Was an inline margin. */
.glow-note--spaced { margin-top: var(--glow-s4); }

/* The unit beside a slider value. Was a three-property inline style inside a
   macro, which is the least discoverable place a font-size can live. */
.glow-slider__u {
  font-weight: 400;
  color: var(--glow-muted);
  font-size: var(--glow-meta);
}

/* A link row that follows prose rather than a card — half the gap. */
.glow-linkrow--tight { margin-top: var(--glow-s2); }

/* The laboratory's own disclaimer, set as a pull quote. data/dutch/REFERENCE.md
   §2: never paraphrase — the wording is the protection, and typography that
   treats it as fine print invites exactly that. 8 Aug: it was 20px serif —
   body copy at title size mid-card, the sharpest instance of the interleaving
   Hemi named. The quote bar, the italic and full-ink now do the "not fine
   print" work at prose size. */
.glow-labquote {
  font-style: italic;
  font-size: var(--glow-pt-prose);
  line-height: 1.6;
  color: var(--glow-ink);
  border-left: 2px solid rgba(109, 41, 50, 0.35);
  padding-left: var(--glow-s3);
  max-width: 58ch;
}
.glow-labquote__src {
  font-size: var(--glow-small);
  color: var(--glow-muted);
}

/* ── FAQ, PORTAL WEIGHT ─────────────────────────────────────────────────────
   Added 5 Aug 2026 for /portal/help.

   The public `.glow-faq` (_page.css:476) is deliberately grand — 22px serif
   questions, 32px of vertical padding each — because on /faq the questions ARE
   the page and there is nothing else competing. In the portal that same rule
   turns twelve short answers into four screens of scrolling, and it re-opens
   the size problem the type pass just closed: 1.375rem is not on the portal
   scale.

   So this restyles the same ELEMENT rather than introducing a second one. The
   markup is a bare <details><summary>, which means the public component's
   __q/__a classes are absent and cannot collide. One class name, two weights,
   scoped by container — the alternative was `.glow-faq--portal` on every one
   of twelve entries, which is a modifier that exists only because two pages
   have different densities. */
.glow-block .glow-faq {
  border-bottom: 1px solid var(--glow-light);
}
.glow-block .glow-faq:last-child { border-bottom: 0; }

.glow-block .glow-faq > summary {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--glow-s3);
  cursor: pointer;
  list-style: none;
  font-size: var(--glow-pt-row);
  font-weight: 500;
  line-height: 1.45;
  color: var(--glow-ink);
  padding: var(--glow-s3) 0;
}
.glow-block .glow-faq > summary::-webkit-details-marker { display: none; }

.glow-block .glow-faq > summary::after {
  content: '+';
  flex: none;
  font-family: var(--glow-font-body);
  /* An icon drawn with a character, not a size on the scale — it is sized to
     sit on the summary's baseline and rotate to a cross. */
  font-size: 1.125rem; /* lint-exempt: glyph */
  line-height: 1;
  color: var(--glow-burgundy);
  transition: transform var(--glow-dur) var(--glow-ease);
}
.glow-block .glow-faq[open] > summary::after { transform: rotate(45deg); }

.glow-block .glow-faq > div {
  padding-bottom: var(--glow-s3);
  max-width: 62ch;
}
.glow-block .glow-faq > div p {
  font-size: var(--glow-small);
  line-height: 1.7;
  color: var(--glow-ink-soft);
  margin: 0 0 var(--glow-s2);
}
.glow-block .glow-faq > div p:last-child { margin-bottom: 0; }
.glow-block .glow-faq > div .glow-note { margin-top: var(--glow-s2); }

/* Numbered instructions — the app install, and nothing else so far. Numerals
   in burgundy because they are the one place in the portal where the ORDER is
   load-bearing: doing step three before step two on an iPhone lands you in the
   wrong menu. */
.glow-steps-list {
  margin: 0 0 var(--glow-s2);
  padding-left: 1.35rem;
  font-size: var(--glow-small);
  line-height: 1.7;
  color: var(--glow-ink-soft);
}
.glow-steps-list li { margin-bottom: 0.4rem; }
.glow-steps-list li::marker { color: var(--glow-burgundy); font-weight: 600; }
.glow-steps-list strong { color: var(--glow-ink); font-weight: 600; }

/* ── THE CREDIT MARK ────────────────────────────────────────────────────────
   Added 5 Aug 2026. Hemi asked for the MPS logo in the rail foot, matching the
   pattern SigmaSync's portal uses — label above, lockup below — "just make it
   the right size and a greyscale".

   THE SIZE IS SET BY THE THINNEST LINE, NOT BY THE MARK. Ink extents measured
   rather than guessed: the triangle is 51% of the lockup's height, MYPIXEL
   17%, and STRATEGY 14.6% — and STRATEGY is a thin outline face, which is the
   first thing to turn to mush. At 5.5rem wide the whole lockup is 62px tall,
   which puts STRATEGY at ~9px: legible, and small enough to stay a credit.

   Going larger would read as co-branding. Going smaller loses the second word,
   and a wordmark with one legible line is worse than no wordmark — the same
   finding as the Glow lockup in the rail well.

   GREYSCALE IS THE POINT, NOT A PREFERENCE. Three saturated triangles in a
   palette of cream, ink and one burgundy would be the most colourful thing on
   the page, sitting at the bottom of the rail, competing with the client's own
   record. 45% opacity matches the text link it replaced. */
.glow-rail__credit-mark {
  display: block;
  line-height: 0;
}

.glow-rail__credit-mark img {
  display: block;
  width: 5.5rem;
  height: auto;
  filter: grayscale(1);
  opacity: 0.45;
  transition: opacity var(--glow-dur) var(--glow-ease);
}

.glow-rail__credit-mark:hover img { opacity: 0.7; }


/* ==========================================================================
   PRINT — the one document a client carries out of here
   ==========================================================================

   Added 5 August 2026.

   WHY THIS EXISTS. A client takes her hormone results to a GP. That is the
   whole reason the results pages are built the way they are — the laboratory's
   own words kept verbatim, the reference ranges shown rather than summarised,
   the nurse's interpretation attributed and dated. All of that is there so the
   page survives being read by a second clinician.

   And then it printed with a navigation rail down the side, a top bar, and
   every disclosure panel collapsed.

   APP 12 gives a client the right to a copy of what an organisation holds
   about her. `/portal/data` makes that requestable. This makes the most
   important one usable the moment she wants it, without asking anybody — which
   is the difference between a right and a process.

   THE DECISIONS ARE ALL THE SAME DECISION: what does a GP need on paper.
   · No rail, no top bar, no collapse chevrons — furniture for a screen.
   · Every <details> forced open. A folded section on paper is a missing
     section, and the folded ones are the in-range markers, which is exactly
     what a second opinion checks first.
   · Backgrounds off, hairlines kept. The reference BAND stays, because a value
     without its range is not a result.
   · Links print their href — a paper page cannot be clicked, and the lab's own
     documents are the most likely thing to be followed up.
   · No orphaned rows: a marker and its range never break across a page.       */

@media print {

  .glow-rail,
  .glow-topbar,
  .glow-whois,
  .glow-portal__scrim,
  .glow-rail__toggle,
  .glow-burger { display: none !important; }

  .glow-portal,
  .glow-portal__main,
  .glow-portal__content {
    display: block !important;
    background: #fff !important;
    padding: 0 !important;
    margin: 0 !important;
    overflow: visible !important;
    height: auto !important;
  }

  body { background: #fff; }

  /* Every disclosure open. A section folded on paper has simply vanished. */
  .glow-sec,
  .glow-faq,
  .glow-hx { display: block !important; }
  .glow-sec > summary::after,
  .glow-sec__chev,
  .glow-faq > summary::after,
  .glow-hx > summary { display: none !important; }
  .glow-sec > summary { padding-inline: 0 !important; }

  /* Card frames become rules. Ink on paper is expensive and a printed portal
     full of tinted panels is a photocopy of a website rather than a document. */
  .glow-block,
  .glow-card,
  .glow-sec,
  .glow-idcard,
  .glow-next a,
  .glow-labnote {
    background: #fff !important;
    border-color: #ccc !important;
    box-shadow: none !important;
    break-inside: auto;
  }

  /* A marker and its range are one object. Splitting them across a page break
     puts a number on one sheet and its meaning on another. */
  .glow-metric,
  .glow-slider,
  .glow-card__row,
  .glow-relby { break-inside: avoid; }

  .glow-block__head,
  .glow-card__head { break-after: avoid; }

  /* The reference band is the only fill that survives — a value without its
     range is not a result, it is a number. */
  .glow-bar__band { background: #e8e8e8 !important; print-color-adjust: exact; }
  .glow-bar__mark { background: #000 !important; print-color-adjust: exact; }
  .glow-metric--out .glow-bar__mark { background: #000 !important; }

  /* Paper has no hover, so a link has to carry its destination. Internal
     portal links are dropped — they are unreachable from paper anyway — and
     only real URLs and the clinic number are spelled out. */
  a[href^="http"]::after,
  a[href^="tel:"]::after {
    content: " (" attr(href) ")";
    font-size: 9pt;
    color: #555;
    word-break: break-all;
  }
  a[href^="tel:"]::after { content: " (" attr(href) ")"; }

  /* Who released it and when is the first thing a GP looks for, so it is not
     allowed to be quiet on paper. */
  .glow-relby { border-left-color: #000 !important; }

  /* Provenance in the footer of every sheet. A printed clinical page with no
     source is worth very little to whoever reads it next. */
  @page { margin: 18mm 16mm; }
}

/* ── Overview furniture ─────────────────────────────────────────────────────
   Added 5 Aug 2026 with the overview rebuild. */

/* The greeting sits ABOVE the h1 and is deliberately smaller than it. She is
   being greeted; the page is still called "your record". Inverting that —
   "Good evening, Sarah" as the h1 — makes the page about the salutation. */
.glow-greeting {
  font-size: var(--glow-small);
  color: var(--glow-muted);
  margin: 0 0 var(--glow-s1);
}

/* The base-details strip. Sits under the lead, above everything that can be
   empty, so the page has one thing on it that is true for every client from
   the first day. A frame rather than a bare grid, because it is a statement of
   record and the frame is what says so. */
.glow-idcard {
  background: var(--glow-white);
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius);
  padding: var(--glow-s2) var(--glow-s3);
  margin-bottom: var(--glow-s4);
}
.glow-idcard .glow-dochdr { border-top: 0; padding-top: 0; }

/* WHAT WE DECIDED AGAINST.
   Set apart from the summary above it on purpose. A refusal is the most
   valuable and most easily skimmed-past line in the whole record, and running
   it on as a second paragraph is exactly how it gets read as a caveat rather
   than as a decision. Burgundy because this is the house position, not a
   warning — there is no red anywhere in this portal. */
.glow-declined {
  margin-top: var(--glow-s3);
  padding-top: var(--glow-s3);
  border-top: 1px solid var(--glow-light);
}
.glow-declined .glow-eyebrow {
  display: block;
  color: var(--glow-burgundy);
  margin: 0 0 var(--glow-s1);
}
.glow-declined p:last-child { margin-bottom: 0; }

@media (max-width: 620px) {
  .glow-idcard { padding: var(--glow-s2); }
}

/* ── THE TRACK — a process drawn as a path ──────────────────────────────────
   Added 5 Aug 2026. Hemi pointed at skinrevision.com.au/#bldm and asked for
   somewhere in the portal that feels "alive" as well as clean.

   What is there is a five-node progress track: a 9px burgundy dot per phase, a
   1px hairline at 25% opacity flexing between them, a very small label beneath
   each. Arrival · Brief · Drop · Journey · Close. It reads as a path rather
   than a paragraph, which is the whole reason it holds attention.

   ─────────────────────────────────────────────────────────────────────────
   ⚠ THE ONE RULE THAT MAKES THIS LEGAL HERE, AND IT IS EASY TO BREAK.

   NO NODE IS EVER MARKED AS "YOURS". Every dot is the same weight, the same
   colour, always.

   The Skin Revision track describes what ONE session contains — it is
   descriptive, and nothing on it is current. The moment a node lights up to
   show where the client has got to, the same component becomes a progress
   indicator with dots instead of a bar, and `CLAUDE.md` bans those outright.

   The distinction is not decorative: a described process invites understanding,
   a tracked one invites a feeling about how far along you are. This portal is a
   case file. It may explain what happens; it may not score her against it.

   `ui.position()` remains the ONLY way a location is ever stated — "Module 6 of
   8", in words, once. Never on this. */
.glow-track {
  display: flex;
  align-items: flex-start;
  margin: var(--glow-s3) 0 0;
}

.glow-track__node {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  flex: none;
}

.glow-track__dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--glow-burgundy);
  flex: none;
}

.glow-track__label {
  /* Eyebrow spec (8 Aug) — weight and tracking match the group; the burgundy
     stays, it is the one label that names where you ARE. */
  font-size: var(--glow-pt-label);
  font-weight: 600;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--glow-burgundy);
  white-space: nowrap;
}

/* The connector. Sits on the dots' centre line, so it is nudged up by half the
   label's height rather than centred on the node as a whole. */
.glow-track__link {
  flex: 1 1 0%;
  height: 1px;
  background: rgba(109, 41, 50, 0.25);
  margin: 4px 6px 0;
}

/* Below ~30rem five nodes cannot hold their labels side by side without the
   words colliding, so the track turns vertical: dots down the left, labels
   beside them, connector becomes the rule between. Same information, and it
   still reads as a sequence rather than a list. */
@container (max-width: 30rem) {
  .glow-track { flex-direction: column; align-items: stretch; }
  .glow-track__node { flex-direction: row; gap: 0.75rem; justify-content: flex-start; }
  .glow-track__link { flex: none; width: 1px; height: 14px; margin: 2px 0 2px 4px; }
}

/* ── The archetype track, given its own row ─────────────────────────────────
   It sat inside the half-width "What this is" card, where four nodes and their
   labels had roughly 300px to share — cramped, and it left the two columns of
   the grid at wildly different heights, which is what actually read as messy.

   Full width under both cards it has room, and the columns balance. */
.glow-arch-track {
  padding: var(--glow-s2) var(--glow-s3) var(--glow-s3);
  border-top: 1px solid var(--glow-border);
  margin-top: var(--glow-s3);
}
/* Equal-height columns so a short card no longer leaves a ragged edge beside
   a long one. */
.glow-portal__grid > .glow-card { align-self: stretch; }

/* ── Prose inside a card row ────────────────────────────────────────────────
   Added 7 Aug 2026. The agreed-visit note on /portal/visits was wearing
   .glow-linkrow, which only sets margins for a row containing a link and
   carries no type scale at all — so a sentence of prose inherited body size
   and sat beside 15px .v values at nearly twice their size.

   "The fonts look off" was accurate and the fonts were not the cause: it was
   prose in a class meant for a link. This is the class it should have had. */
.glow-rowtext {
  margin: var(--glow-s2) 0 0;
  font-size: var(--glow-pt-prose);
  line-height: 1.55;
  color: var(--glow-ink);
  max-width: 62ch;
}

/* ── The script's twelve months ─────────────────────────────────────────────
   A bar across a REAL, DEFINED period — issued to expiry, both endpoints
   stored on the contact. Permitted by the 7 Aug technology ruling precisely
   because the period exists and is recorded; a bar with invented endpoints, or
   one scoring how much has been "used", would not be.

   Deliberately unlabelled with any percentage. The client does not need a
   number; she needs to see roughly where she sits. */
/* ---- LEGACY script bar — MOCKUP ONLY, DO NOT REUSE ----------------------
   ⚠ The live portal no longer uses this. portal-index.html renders the script
   and the plan through ui.span(), which shares the results-row geometry and
   computes its position from the real dates.

   This block survives solely for templates/review-journey.html, a static
   non-live mockup. Its `width: 78%` below is HARDCODED — it was rendering to
   every client, identically, forever, under the words "this is where yours
   sits". Acceptable in a mockup; it was never acceptable on the record.
   Retained, fenced, and labelled so the next person cannot mistake it for a
   working component. 30 Aug 2026. */
.glow-script { padding: var(--glow-s3) 0 0; }
.glow-script__ends {
  display: flex; justify-content: space-between; gap: var(--glow-s2);
  font-size: var(--glow-pt-label); font-weight: 600; letter-spacing: 0.11em;
  text-transform: uppercase; color: var(--glow-muted); margin-bottom: 0.6rem;
}
.glow-script__ends b { font-weight: 600; color: var(--glow-ink); }
.glow-script__bar {
  position: relative; height: 6px; border-radius: 3px;
  background: var(--glow-blush, #f0e6e4); overflow: hidden;
}
.glow-script__fill {
  position: absolute; inset: 0 auto 0 0; width: 78%;
  background: var(--glow-deep, #4a1f24); border-radius: 3px;
}
.glow-script__bar--revoked .glow-script__fill { width: 100%; background: var(--glow-muted); }
.glow-script__note {
  margin: 0.75rem 0 0; font-size: var(--glow-pt-row);
  line-height: 1.55; color: var(--glow-ink-soft, var(--glow-muted)); max-width: 62ch;
}


/* ---- THE MODULE LIST — Inside Out -----------------------------------------
   Hemi, 8 Aug: buttons for each module, greyed until released. Then: compare it
   with the Inside Out holding page and the course itself.

   REBUILT AS ROWS, NOT TILES. The first version was a five-across tile grid,
   which was mine and matched nothing. The Skin Code's own module list is
   full-width numbered rows — "01  Hormone Foundations / Available" with an
   arrow — and Glow's portal ALREADY has that shape in .glow-index__row on the
   "Also available" block. Two independent surfaces converging on the same
   pattern is the answer: a client moving from this page into the course should
   not experience a change of grammar at the boundary.

   Status wording follows the course too — "Available", not "Open". Same object,
   same word, on both sides of a handover between two companies.

   PERMITTED because the course is a real thing of fixed length and the client is
   somewhere inside it (CLAUDE.md). A locked row says NOT YET. No score, no
   percentage, nothing fires because someone stopped.

   LOCKED IS INERT BY MARKUP, not by CSS. An open module is an <a>; a locked one
   is a <span> with no href, so it cannot be clicked, tabbed to, or announced as
   a control. Styling something to look disabled while leaving it operable is the
   usual version of this bug. */
.glow-mods {
  list-style: none;
  margin: var(--glow-s4) 0 0;
  padding: 0;
  display: grid;
  gap: var(--glow-s1);
}

/* The row.

   ⚠ align-items: BASELINE, not center. Centring vertically centres the index
   against the whole two-line block, which floats it into the gap between the
   title and the status and reads as three unrelated elements sharing a box.
   On the baseline, "01" and "Hormone Foundations" sit on one line and the row
   reads as one thing with a note under it. That is the entire difference
   between this and the reference, and it is one property.

   Padding is s3, not s4. The first pass was generous on the theory that air
   was what made the reference good; air is not what made it good, alignment
   was, and the extra height just made nine rows into a long scroll. */
.glow-mod > a,
.glow-mod:not(.glow-mod--open) {
  display: flex;
  align-items: baseline;
  gap: var(--glow-s3);
  padding: var(--glow-s2) var(--glow-s3);
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius);
  text-decoration: none;
}

/* The index. Tabular so nine rows align on the numeral, not on its shape. */
.glow-mod__i {
  font-size: var(--glow-meta);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.11em;
  color: var(--glow-burgundy);
  opacity: 0.75;
  flex: none;
  min-width: 1.9em;
}

.glow-mod__b { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.glow-mod__n { font-size: var(--glow-pt-row); font-weight: 500; line-height: 1.3; color: var(--glow-ink); }
.glow-mod__s { font-size: var(--glow-meta); color: var(--glow-muted); }
.glow-mod__a { margin-left: auto; color: var(--glow-burgundy); flex: none; opacity: 0.8; }

.glow-mod--open > a { background: #fff; transition: border-color .15s ease; }
.glow-mod--open > a:hover { border-color: var(--glow-burgundy); }

/* Where she is now. One quiet marker, not a highlight competing with eight
   others. */
.glow-mod--here > a { border-color: var(--glow-burgundy); box-shadow: inset 2px 0 0 var(--glow-burgundy); }
.glow-mod--here .glow-mod__s { color: var(--glow-burgundy); }

/* Not yet. Reads as unfinished shape, never as failure — no strike-through, no
   padlock, no red. */
.glow-mod--locked { background: transparent; border-style: dashed; }
.glow-mod--locked .glow-mod__i { opacity: 0.4; }
.glow-mod--locked .glow-mod__n { color: var(--glow-muted); font-weight: 400; }

/* ── THE JOURNEY ────────────────────────────────────────────────────────────
   13 Aug 2026. `handoff/JOURNEY-TIMELINE.md` — the B3 "skin journey" surface.

   TWO LAYERS, DRAWN DIFFERENTLY, AND THE DIFFERENCE IS THE POINT.

     A FILLED dot is a RECORD.    Signed, immutable, it will never move.
     A HOLLOW dot is a PLAN ITEM. Living — it can be added, changed, removed.

   A client should be able to tell at a glance which parts of her page are
   history and which are intention, without being told. That is the whole
   design brief, and it is why the spine is one column rather than two.

   ⚠ WHAT THIS COMPONENT MUST NEVER GROW
   No percentage. No bar. No "overdue", no red, no countdown, no colour that
   means late. A suggested window that turns red is a scoreboard, and
   `PORTAL.md` §4c bans those. The strongest state here is `--quiet`, which
   is greyer, not louder.

   `.glow-jn--removed` strikes ONLY the title, never the whole row: the item
   stays legible because "what happened to the peel we talked about?" needs an
   answer, not an absence. Removal is a status, not a deletion. */
.glow-journey {
  display: flex;
  flex-direction: column;
  margin: var(--glow-s3) 0 0;
}

/* The row. The spine is drawn by the ::before rule on the marker column so it
   connects continuously between rows without a wrapper per segment. */
.glow-jn {
  display: grid;
  grid-template-columns: 1.25rem 1fr;
  gap: 0 0.875rem;
  padding: 0 0 var(--glow-s2);
  position: relative;
}

.glow-jn:last-child { padding-bottom: 0; }

/* Marker column: the connector, then the dot on top of it. */
.glow-jn__mark {
  position: relative;
  display: flex;
  justify-content: center;
  padding-top: 0.34rem;
}

.glow-jn__mark::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: -0.5rem;
  left: 50%;
  width: 1px;
  background: rgba(109, 41, 50, 0.22);
  transform: translateX(-50%);
}

.glow-jn:last-child .glow-jn__mark::before { display: none; }

.glow-jn__dot {
  position: relative;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--glow-burgundy);
  flex: none;
}

/* A plan item — hollow. Not weaker, just not yet a fact. */
.glow-jn--plan .glow-jn__dot {
  background: var(--glow-cream);
  box-shadow: inset 0 0 0 1.5px var(--glow-burgundy);
}

.glow-jn__body { min-width: 0; display: flex; flex-direction: column; gap: 0.2rem; }

.glow-jn__date {
  font-size: var(--glow-eyebrow-size);
  font-weight: var(--glow-eyebrow-weight);
  letter-spacing: var(--glow-eyebrow-track);
  text-transform: uppercase;
  color: var(--glow-burgundy);
}

/* Undated is a real state and it is written, never guessed at. */
.glow-jn__date--open { color: var(--glow-muted); }

.glow-jn__t {
  font-size: var(--glow-pt-row);
  font-weight: 500;
  line-height: 1.35;
  color: var(--glow-ink);
}

.glow-jn__note { font-size: var(--glow-meta); color: var(--glow-muted); line-height: 1.45; }

/* The sentence a plan item came from. Quoted, in the RN's words, because an
   item that cannot cite one should not exist — TREATMENT-RECORD-SCHEMA §2.3. */
.glow-jn__quote {
  font-size: var(--glow-meta);
  color: var(--glow-ink-soft);
  font-style: italic;
  border-left: 2px solid var(--glow-border);
  padding-left: 0.6rem;
  margin-top: 0.1rem;
}

/* An addendum. Indented under the record it corrects, never merged into it —
   the reader must see both the original and the correction. */
/* The addendum keeps the SAME marker column, so its dot sits on the one
   continuous spine and the BODY is what indents. Drawn first with an indented
   marker column (2.75rem), which gave the addendum its own connector stub
   floating beside a broken spine — subordination expressed by moving the dot
   off the line it belongs to. The record it corrects is on that line; so is it. */
.glow-jn--addendum .glow-jn__body { padding-left: 1.5rem; }
.glow-jn--addendum .glow-jn__dot { width: 5px; height: 5px; opacity: 0.55; }
.glow-jn--addendum .glow-jn__t { font-size: var(--glow-meta); color: var(--glow-ink-soft); font-weight: 400; }

.glow-jn--removed .glow-jn__t { text-decoration: line-through; color: var(--glow-muted); font-weight: 400; }
.glow-jn--removed .glow-jn__dot { background: var(--glow-cream); box-shadow: inset 0 0 0 1.5px var(--glow-border); }

/* ⚠ NO .glow-script HERE. It already exists above (line ~2470, "The script's
   twelve months") and a second definition this far down the file would win the
   cascade and silently restyle the live /portal bar.

   Caught 13 Aug while building /review/journey: the duplicate was written and
   removed the same hour. The journey page uses the EXISTING component and its
   existing markup, which is why it needs no width of its own. */

/* ---- THE ARCHETYPE DIAL ---------------------------------------------------
   Ported from Skin Revision 30 Aug 2026. Geometry in
   partials/archetype-dial.html; colours come from archetype-data.html.

   The original sits on a near-black field (#2C2C2C) where six saturated wedges
   read as jewel tones. Glow's portal ground is cream, so the same colours sit
   at higher luminance against a lighter field — they need no adjustment, but
   the hub and the gaps do: on dark the gaps read as shadow, on cream they must
   read as the page showing through. Hence a cream hub and cream gaps rather
   than the original's dark ones.

   No JavaScript. Hover is CSS; the framer-motion entrance is not ported,
   because a record page that animates on every visit is a page that costs
   attention every visit. */
.glow-dial { width: 100%; max-width: 20rem; margin: 0 auto; }

/* ---- THE DIAL AS A FEATURE — homepage, 30 Aug 2026 ---------------------
   Measured on the live page: the dial at its default 20rem sat in a 581px
   column beside a 599px column of copy, leaving 214px of dead height and a
   graphic that looked lost rather than deliberate.

   Same fault as the archetype track on /skin-code (7 Aug, "About your
   archetype looks messy"): one column carrying real content, the other
   carrying something too small for the space it was given. Unequal column
   height is what reads as "unbalanced" — it is almost never the styling. */
.glow-dialfeature {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
}
.glow-dialfeature .glow-dial { max-width: 30rem; }
.glow-dial svg { width: 100%; display: block; overflow: visible; }

.glow-dial__seg { cursor: default; }
.glow-dial__wedge {
  stroke: var(--glow-surface);
  stroke-width: 3;
  transition: opacity var(--glow-dur) var(--glow-ease),
              transform var(--glow-dur) var(--glow-ease);
  transform-origin: 200px 200px;
}
/* The symbols arrive as line art whose stroke was rewritten to currentColor in
   the port, so one declaration recolours all six. fill:none is set on the group
   in the markup — without it every path defaults to solid black and the icons
   render as blobs. */
.glow-dial__sym { color: var(--glow-white); pointer-events: none; }
.glow-dial__sym path,
.glow-dial__sym circle,
.glow-dial__sym line { stroke: currentColor; }

/* Hovering one wedge quietens the others. On a dial of six this reads as
   "look here" without moving anything — the original scaled the segment, which
   on a clinical page is more motion than the information warrants. */
/* ⚠ POINTER-GATED. Unguarded, a phone that emulates hover keeps the state
   after a tap — five wedges stay dimmed with nothing to un-dim them, and the
   dial looks broken on the devices that carry most of this traffic (Search
   Console: 259 mobile clicks to desktop's 143). Touch simply gets all six at
   full strength, which is the correct resting state anyway. */
@media (hover: hover) and (pointer: fine) {
  .glow-dial:hover .glow-dial__wedge { opacity: 0.45; }
  .glow-dial__seg:hover .glow-dial__wedge { opacity: 1; }
}

/* HER archetype, when one is known. A ring rather than a fill change, so the
   colour still reads true — and it is a statement of fact, not a score. */
.glow-dial__seg.is-mine .glow-dial__wedge {
  stroke: var(--glow-ink);
  stroke-width: 4;
}
@media (hover: hover) and (pointer: fine) {
  .glow-dial:hover .glow-dial__seg.is-mine .glow-dial__wedge { opacity: 1; }
}

.glow-dial__hub { fill: var(--glow-surface); }
.glow-dial__hub-t {
  text-anchor: middle;
  font-family: var(--glow-font-body);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: var(--glow-eyebrow-track);
  fill: var(--glow-text-muted);
}

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

/* ==========================================================================
   Glow Cosmetique — the health declaration form
   Contract: templates/partials/portal-form.html · js/intake.js
   ==========================================================================

   The only writing surface in the portal. Everything else here is a record
   the client reads; this is the one page she fills in, and it carries the
   only progress indicator in the product.

   ─────────────────────────────────────────────────────────────────────────
   THE PROGRESS BAR, AND WHY THERE IS ONE HERE AND NOWHERE ELSE

   _portal.css opens by saying progress bars and percentages are "deliberately
   NOT borrowed", and ui.position() says outright: "no percentage and no bar —
   a percentage is a score, a bar is a scoreboard." Both stand. This file does
   not overturn them, it locates the edge they were drawn without.

     A progress indicator that SURVIVES THE SESSION is a score.
     One that DIES WITH IT is a scrollbar.

   "Your file is 60% complete", sitting on her record, is a standing judgement
   she carries around between visits. A bar across six steps of a form she is
   filling in right now answers one question — *how much more of this is
   there* — and then it is gone. That is the most reasonable question anyone
   has about a medical form, and refusing to answer it is not restraint, it is
   just worse.

   CLAUDE.md, 7 August, permits exactly this and names it: "progress through a
   real, defined thing — a form being filled." Six steps is a real, defined
   thing with a real length.

   SO THE RULES THIS FILE KEEPS:
     · The bar exists ONLY inside .glow-form. Never on a tile, never in the
       rail, never on the overview, never against her name.
     · It measures the FORM, never the client. No "profile completeness".
     · No numeric percentage. The bar already carries the proportion; a number
       beside it is the same fact rendered as a score, and a score invites a
       comparison there is nobody to make.
     · "Step 2 of 6" in words, because that is what ui.position() would say.

   ─────────────────────────────────────────────────────────────────────────
   AND THE THREE-STATE CONTROL IS THE OTHER LOAD-BEARING THING HERE

   Yes / No / Not sure, everywhere, and NOTHING is pre-selected. An unanswered
   question and a "no" must never look alike — see INTAKE.md rule 2, and C-06:
   "a blank field and a field marked 'not recorded' look identical to a person
   and completely different to the page."

   There is no "mark all as no" control and there is no CSS for one.
   ========================================================================== */


/* THE SURFACE. Added 30 Aug 2026 — this was the only module in the portal
   without one. The form sat directly on the ground, so the blush "why" block
   had nothing to sit on and the only visible edge was the focus ring that
   intake.js puts on the active step. Hemi: "it seems like there are major
   things missing" — the missing thing was the card every other module has.

   Same surface as .glow-block: white, one hairline, --glow-radius-ui. B-08 §3. */
.glow-form {
  margin-top: var(--glow-s2);
  background: var(--glow-surface);
  border: 1px solid var(--glow-surface-border);
  border-radius: var(--glow-radius-ui);
  padding: var(--glow-card-pad);
}


/* The form cannot be submitted — said at the top, before anyone fills it in.
   Amber, because the worst case in this portal is amber and never red, and
   because this is the one signal colour on the page. */
.glow-form__notice {
  font-family: var(--glow-font-body);
  font-size: var(--glow-pt-prose);
  line-height: 1.55;
  color: var(--glow-ink);
  background: var(--glow-flag-soft);
  border: 1px solid var(--glow-flag-line);
  border-radius: var(--glow-radius-ui);
  padding: var(--glow-s3);
  margin: 0 0 var(--glow-s3);
}


/* ==========================================================================
   PROGRESS — read the block comment above before changing anything here
   ========================================================================== */

/* ⚠ REGRESSION FIXED 30 Aug 2026. This is sticky, so it needs an opaque fill to
   stop content scrolling under it — and it was painted --glow-pt-ground, which
   was right while the form sat directly on the ground. The moment .glow-form
   became a white card, this became a grey slab inside a white box: the thing
   Hemi saw. It paints the surface it now sits on. */
.glow-form__prog {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--glow-surface);
  padding: var(--glow-s3) 0 var(--glow-s1h);
  /* No border-bottom. I added one on 30 Aug and it stacked a third horizontal
     rule under the progress bar and above the step edge — the bar IS the
     separator. A divider under a divider is not hierarchy. */
  margin-bottom: var(--glow-s2);
}

/* EYEBROW ABOVE, TITLE BELOW — the pattern every other module in the portal
   uses (B-08 §2), and this was the one place not using it.

   It was a flex row with justify-content: space-between, which pushed "STEP 1
   OF 6" and "About you" to opposite ends of a 768px card — two halves of one
   phrase with ~600px of nothing between them, so the eye had to cross the whole
   card to join them up. The type was already right (--glow-pt-row 15/600, the
   same as a tile fact and a record value); the ARRANGEMENT was the imbalance.

   Hemi: "it is things like this that make me feel like we are unbalanced so
   often." Stacked, they read as one label. 30 Aug 2026. */
.glow-form__pos {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--glow-s0);
  margin-bottom: var(--glow-s1h);
}

/* "Step 2 of 6" — the eyebrow treatment, one size, like every other label. */
.glow-form__step-n {
  font-family: var(--glow-font-body);
  font-size: var(--glow-pt-label);
  font-weight: 600;
  letter-spacing: var(--glow-eyebrow-track);
  text-transform: uppercase;
  color: var(--glow-muted);
}

/* The section name. Weight carries the rank, not size. */
.glow-form__step-name {
  font-family: var(--glow-font-body);
  font-size: var(--glow-pt-row);
  font-weight: 600;
  color: var(--glow-ink);
}

.glow-form__track {
  position: relative;
  /* 2px read as a hairline divider, not as progress — a progress indicator you
     cannot see is the same as not having one. 30 Aug 2026. */
  height: 5px;
  background: var(--glow-surface-alt);
  border-radius: var(--glow-radius-sm);
  overflow: hidden;
}

.glow-form__fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: var(--glow-action);
  border-radius: var(--glow-radius-sm);
  transition: width 220ms ease;
}

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


/* ==========================================================================
   A STEP
   ========================================================================== */

.glow-fstep { display: none; }
.glow-fstep.is-active { display: block; }

/* ⚠ THE BOX INSIDE THE BOX. intake.js:167 gives the active step tabindex="-1"
   and focuses it, so a keyboard or screen-reader user lands in the new content
   instead of on a button that just moved. That is correct and stays.

   What was wrong is what it LOOKED like: the global ring is 2px solid burgundy
   at 3px offset, designed for a button — drawn around a whole step region
   inside a white card it read as a second, heavier border, which is what made
   the form look like two nested boxes.

   A focus ring on a REGION is not an affordance — nothing inside it can be
   activated by pressing enter, and the user did not navigate here: the script
   moved focus for them. So the ring is removed, not merely quietened. I tried
   quietening it first and it still drew a full-height box around the step,
   misaligned with the card by the outline's own geometry — a phantom container
   with no meaning.

   ⚠ THE FOCUS ITSELF STAYS. tabindex="-1" and .focus() remain, so a screen
   reader still announces the new step and the tab order still resumes inside
   it. What is removed is only the paint. WAI-ARIA practices allow this where
   focus is moved programmatically for orientation rather than by the user. */
.glow-fstep:focus,
.glow-fstep:focus-visible { outline: none; }


/* The same bar, on the overview's "Before your visit" card. Only ever drawn
   when a saved draft exists — see the guard in js/intake.js. An untouched form
   shows no bar, because a 0% bar is a scoreboard reading zero. */
.glow-declprog { margin: var(--glow-s2) 0; }

/* Review harness only — the live bar gets its width from js/intake.js, which
   needs a real draft. templates/review-overview-card.html. */
.glow-form__fill--demo { width: 33.33%; }

.glow-declprog__pos {
  font-family: var(--glow-font-body);
  font-size: var(--glow-pt-label);
  font-weight: 600;
  letter-spacing: var(--glow-eyebrow-track);
  text-transform: uppercase;
  color: var(--glow-muted);
  margin: 0 0 var(--glow-s1);
}


/* A section inside a step: heading, then the reason it is being asked.
   THE REASON LINE IS NOT DECORATION — INTAKE.md rule 1. It is the single
   biggest difference between this form and every other clinical form in the
   market, and a section without one is unfinished. */
.glow-fsec { margin-bottom: var(--glow-s3); }

/* WHY THIS SECTION IS ASKED. Now that .glow-form is a white card, a filled
   blush box inside it was a fourth surface on one screen — ground, card, tint,
   field. It reads as an aside instead: the accent lives in a rule down the
   left, which is how the rest of the portal marks a note (.glow-note). One
   less surface, same emphasis. 30 Aug 2026. */
.glow-fsec__why {
  font-family: var(--glow-font-body);
  font-size: var(--glow-pt-prose);
  line-height: 1.55;
  color: var(--glow-text-soft);
  border-left: 2px solid var(--glow-surface-border);
  padding: 0 0 0 var(--glow-s2);
  margin: 0 0 var(--glow-s2);
}


/* ==========================================================================
   A QUESTION
   ========================================================================== */

/* Questions separate with a rule BETWEEN them, never above the first one.

   ⚠ This was `.glow-q:first-of-type`, which worked only by accident: it means
   "first element of its TAG among siblings", and it happened to be true because
   f.why() emits a <p> while f.text()/f.tri() emit <div>. Put any other div
   above the first question — a wrapper, a fieldset, an alert — and the rule
   silently stops matching and a stray line appears above the first field.

   The adjacent-sibling form says what is actually meant: a rule goes between
   two questions. It cannot be broken by anything placed above them. */
.glow-q { padding: var(--glow-s2) 0; }
.glow-q + .glow-q { border-top: 1px solid var(--glow-light); }

.glow-q__label {
  display: block;
  font-family: var(--glow-font-body);
  font-size: var(--glow-pt-row);
  font-weight: 500;
  line-height: 1.45;
  color: var(--glow-ink);
  margin-bottom: var(--glow-s1h);
}

/* A per-question aside — smaller than the section reason, used where one row
   needs a clause the section line cannot carry. */
.glow-q__hint {
  display: block;
  font-size: var(--glow-small);
  font-weight: 400;
  line-height: 1.5;
  color: var(--glow-muted);
  margin-top: var(--glow-s0);
}

/* The conditional follow-up. Indented and ruled so it reads as belonging to
   the answer above it rather than as a new question. */
.glow-q__sub {
  display: none;
  margin-top: var(--glow-s2);
  padding-left: var(--glow-s2);
  border-left: 2px solid var(--glow-border);
}
.glow-q__sub.is-open { display: block; }


/* ==========================================================================
   THE THREE-STATE CONTROL
   Segmented, equal thirds, nothing pre-selected.
   Selected = tint fill + accent text. No weight change — same rule as the
   rail's active state, so selection reads identically everywhere.
   ========================================================================== */

.glow-tri {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius-sm);
  overflow: hidden;
  max-width: 26rem;
}

.glow-tri__opt { position: relative; display: block; }

/* The input is the state; the label is the surface. Not display:none —
   that removes it from the accessibility tree and from keyboard order. */
.glow-tri__in {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}

.glow-tri__lab {
  display: block;
  text-align: center;
  padding: var(--glow-s1h) var(--glow-s1);
  font-family: var(--glow-font-body);
  font-size: var(--glow-small);
  font-weight: 500;
  color: var(--glow-ink-soft);
  background: var(--glow-white);
  border-left: 1px solid var(--glow-border);
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.glow-tri__opt:first-child .glow-tri__lab { border-left: 0; }

.glow-tri__in:hover + .glow-tri__lab { background: var(--glow-cream); }

.glow-tri__in:checked + .glow-tri__lab {
  background: var(--glow-blush);
  color: var(--glow-burgundy);
}

.glow-tri__in:focus-visible + .glow-tri__lab {
  outline: 2px solid var(--glow-burgundy);
  outline-offset: -2px;
}

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


/* ==========================================================================
   TEXT INPUTS
   ========================================================================== */

.glow-txt {
  display: block;
  width: 100%;
  /* Was max-width: 32rem inside a column already capped at 48rem by
     portal_width="reading" — so every field stopped two-thirds across and the
     right of the card was permanently empty. The column is the constraint;
     the field fills it. 30 Aug 2026. */
  font-family: var(--glow-font-body);
  font-size: var(--glow-pt-prose);
  color: var(--glow-ink);
  background: var(--glow-white);
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius-ui);
  padding: var(--glow-field-pad);
}

.glow-txt:focus {
  outline: none;
  border-color: var(--glow-burgundy);
}

textarea.glow-txt { min-height: 5.5rem; resize: vertical; }

.glow-fgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: var(--glow-s2);
  max-width: 32rem;
}

.glow-flabel {
  display: block;
  font-family: var(--glow-font-body);
  font-size: var(--glow-pt-label);
  font-weight: 600;
  letter-spacing: var(--glow-eyebrow-track);
  text-transform: uppercase;
  color: var(--glow-muted);
  margin-bottom: var(--glow-s0);
}


/* ==========================================================================
   THE MEDICATION LIST
   Objects, not a text box. Each one ENDS — it is never deleted.
   ==========================================================================

   ⚠ THERE IS NO DELETE BUTTON ON A SAVED ROW AND THERE MUST NOT BE ONE.
   Ending a medication preserves that it was taken and when it stopped;
   deleting it destroys that. .glow-med__end is the only affordance on a saved
   row, and .glow-med.is-ended is what it produces. The trash icon on an
   unsaved draft row is a different thing — that row was never a fact. */

.glow-meds { margin-top: var(--glow-s1); }

.glow-med {
  display: flex;
  align-items: baseline;
  gap: var(--glow-s2);
  padding: var(--glow-s1h) var(--glow-s2);
  background: var(--glow-white);
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius);
  margin-bottom: var(--glow-s1);
}

.glow-med__id { flex: 1 1 auto; min-width: 0; }

.glow-med__n {
  display: block;
  font-family: var(--glow-font-body);
  font-size: var(--glow-pt-row);
  font-weight: 500;
  color: var(--glow-ink);
}

.glow-med__d {
  display: block;
  font-size: var(--glow-meta);
  color: var(--glow-muted);
  margin-top: var(--glow-s0);
}

.glow-med__end {
  flex: none;
  font-family: var(--glow-font-body);
  font-size: var(--glow-small);
  font-weight: 500;
  color: var(--glow-burgundy);
  background: none;
  border: 0;
  padding: var(--glow-s0) 0;
  cursor: pointer;
}
.glow-med__end:hover { text-decoration: underline; }

/* An ended medication stays on the list, quiet and legible. It is history,
   and history is the reason it is still here. */
.glow-med.is-ended { background: var(--glow-cream); }
.glow-med.is-ended .glow-med__n { color: var(--glow-muted); }

.glow-med__add {
  font-family: var(--glow-font-body);
  font-size: var(--glow-small);
  font-weight: 500;
  color: var(--glow-burgundy);
  background: none;
  border: 1px dashed var(--glow-border);
  border-radius: var(--glow-radius-ui);
  padding: var(--glow-s1h) var(--glow-s2);
  width: 100%;
  cursor: pointer;
}
.glow-med__add:hover { background: var(--glow-cream); }

.glow-med__draft {
  border: 1px solid var(--glow-border);
  border-radius: var(--glow-radius);
  background: var(--glow-white);
  padding: var(--glow-s2);
  margin-bottom: var(--glow-s1);
}

.glow-med__row {
  display: flex;
  gap: var(--glow-s1);
  margin-top: var(--glow-s2);
}

.glow-med__dates { margin-top: var(--glow-s2); }


/* ==========================================================================
   A LABELLED GROUP — a heading and its clause, above a sub-component
   ==========================================================================

   Used where a block (the medication list, the contact preferences) needs a
   title and a sentence but is not a question and must not wear .glow-q.
   Exists as a class rather than three margins written inline: a style
   attribute is invisible to the next author, which is how the portal got to
   twelve font sizes. See scripts/portal-type-lint.sh check 2. */

.glow-fgroup { margin-bottom: var(--glow-s3); }
.glow-fgroup--top { margin-top: var(--glow-s4); }

.glow-fgroup__label {
  display: block;
  font-family: var(--glow-font-body);
  font-size: var(--glow-pt-row);
  font-weight: 500;
  line-height: 1.45;
  color: var(--glow-ink);
}

.glow-fgroup__hint {
  display: block;
  font-size: var(--glow-small);
  line-height: 1.5;
  color: var(--glow-muted);
  margin: var(--glow-s0) 0 var(--glow-s1h);
}


/* ==========================================================================
   SIGNATURE
   ========================================================================== */

.glow-sign { margin-top: var(--glow-s2); }

.glow-consent {
  display: flex;
  align-items: flex-start;
  gap: var(--glow-s1h);
  padding: var(--glow-s2) 0;
  border-top: 1px solid var(--glow-light);
}
.glow-consent:first-of-type { border-top: 0; }

.glow-consent input[type="checkbox"] {
  flex: none;
  margin-top: 0.2rem;
  width: 1.05rem;
  height: 1.05rem;
  accent-color: var(--glow-burgundy);
}

.glow-consent__t {
  font-family: var(--glow-font-body);
  font-size: var(--glow-pt-prose);
  line-height: 1.55;
  color: var(--glow-ink);
}

.glow-consent__t b { font-weight: 600; }

.glow-consent__opt {
  display: block;
  font-size: var(--glow-meta);
  letter-spacing: var(--glow-eyebrow-track);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--glow-muted);
  margin-top: var(--glow-s0);
}

/* ── THE ONE INVITATION ON THE FORM ──────────────────────────────────────
   The optional updates opt-in, on a blush ground so it reads as Glow talking
   to her rather than as another box to clear. Same ground as .glow-fsec__why,
   deliberately — that is already the surface this form uses when the clinic is
   explaining something rather than asking something.

   ⚠ DISTINCT, NOT LOUD, AND THE DIFFERENCE IS THE RULE. No accent fill, no
   coloured border, no button, no icon, nothing that moves. PORTAL-MAP's line
   holds: "a quiet, factual link where something is genuinely relevant" is
   permitted; "a button, a banner, or anything styled to persuade" is not. A
   tint that says *this one is an offer of something* is the first. A coloured
   call-to-action would be the second.

   ⚠ AND IT IS NEVER PRE-TICKED. Not here, not ever. Express consent is
   required under the Spam Act 2003 and a pre-ticked box is not express — and
   it would break INTAKE.md's own rule that she acts on each fact. There is no
   rule in this file that could fake a checked state, and there must not be. */
.glow-consent--invite {
  background: var(--glow-blush);
  border-top: 0;
  border-radius: var(--glow-radius);
  padding: var(--glow-s2);
  margin-top: var(--glow-s2);
}

.glow-consent--invite .glow-consent__opt { color: var(--glow-muted); }


/* A consent row whose wording does not exist yet. It renders as a visible
   hole rather than as drafted text — INTAKE.md §4 Step 6, photography.
   Inventing a consent clause for the highest-risk data in the business is
   worse than not asking yet, and a gap you can see is the only honest way to
   ship around it. */
.glow-consent--pending {
  background: var(--glow-flag-soft);
  border: 1px dashed var(--glow-flag-line);
  border-radius: var(--glow-radius);
  padding: var(--glow-s2);
  color: var(--glow-ink-soft);
  font-size: var(--glow-small);
  line-height: 1.55;
}


/* ==========================================================================
   NAVIGATION BETWEEN STEPS
   ========================================================================== */

.glow-fnav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--glow-s2);
  margin-top: var(--glow-s3);
  padding-top: var(--glow-s2);
  border-top: 1px solid var(--glow-border);
}

.glow-fbtn {
  font-family: var(--glow-font-body);
  font-size: var(--glow-small);
  font-weight: 500;
  padding: 0.6875rem var(--glow-s3);
  border-radius: var(--glow-radius-ui);
  line-height: 1.2;
  border: 1px solid var(--glow-border);
  background: var(--glow-white);
  color: var(--glow-ink);
  cursor: pointer;
}
.glow-fbtn:hover { background: var(--glow-cream); }

.glow-fbtn--go {
  background: var(--glow-burgundy);
  border-color: var(--glow-burgundy);
  color: var(--glow-white);
}
.glow-fbtn--go:hover { background: var(--glow-deep); border-color: var(--glow-deep); }

.glow-fbtn[hidden] { display: none; }

/* Saved-a-moment-ago. Never a count, never a tick tally — it says the work is
   safe, which is the only thing a save indicator is for. */
.glow-fsaved {
  font-size: var(--glow-meta);
  color: var(--glow-muted);
}


/* ==========================================================================
   THE SUBMITTED STATE
   ========================================================================== */

.glow-done {
  text-align: center;
  padding: var(--glow-s4) var(--glow-s2);
}

.glow-done__t {
  font-family: var(--glow-font-display);
  font-size: var(--glow-h3);
  color: var(--glow-ink);
  margin: 0 0 var(--glow-s1);
}

.glow-done__b {
  font-size: var(--glow-pt-prose);
  line-height: 1.6;
  color: var(--glow-ink-soft);
  max-width: 34rem;
  margin: 0 auto;
}

/* The payload preview. QA HARNESS ONLY — the gated page never renders it. */
.glow-payload {
  text-align: left;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--glow-meta);
  line-height: 1.5;
  white-space: pre;
  overflow-x: auto;
  background: var(--glow-deep);
  color: #E8DED6;
  border-radius: var(--glow-radius);
  padding: var(--glow-s2);
  margin-top: var(--glow-s3);
}


/* ==========================================================================
   Platform override layer — MUST be the last thing in main.css
   ==========================================================================

   HubSpot generates a stylesheet from the theme settings (fields.json) and
   injects it INLINE in <head> via standard_header_includes — which lands after
   our linked main.css. At equal specificity the later rule wins, so the
   boilerplate's defaults were beating the entire Glow design system.

   What it was doing, measured on the live page:
     html   font-size: 24px          <- inflated every rem by 50%
     body   font-family: Lato        <- and colour, and 24px text
     a      underline, #0270E0       <- blue underlined links everywhere
     h1-h6  Merriweather, bold
     button brown fill, 6px radius, 22px text

   The 24px root was the worst of it: it broke the type scale and pushed the
   header nav 152px past the viewport.

   Two defences, because either alone is brittle:
     1. fields.json defaults now describe the Glow system, so the generated CSS
        mostly agrees rather than fights.
     2. This layer re-asserts the essentials one specificity step higher
        (`:root` over `html`, `html body` over `body`, `html a` over `a`) so a
        theme setting changed in the HubSpot UI can never silently wreck the
        design again.

   Specificity, not !important — these need to stay overridable by real
   component rules.
   ========================================================================== */

/* Root — the critical one. `:root` is (0,1,0) and beats `html` at (0,0,1). */
:root {
  font-size: 100%;
}

html body {
  font-family: var(--glow-font-body);
  font-size: var(--glow-body);
  font-weight: 400;
  line-height: 1.7;
  color: var(--glow-ink-soft);
  background: var(--glow-cream);
}

/* Links — kill the injected underline and blue. Components restore any
   decoration they want (.glow-availability keeps its border-bottom rule). */
html a,
html a:hover,
html a:focus,
html a:active {
  color: inherit;
  font-family: inherit;
  font-weight: inherit;
  text-decoration: none;
}

/* Display type */
html h1, html h2, html h3, html h4, html h5, html h6 {
  font-family: var(--glow-font-display);
  font-weight: 400;
  color: var(--glow-ink);
  letter-spacing: -0.015em;
  line-height: 1.06;
  text-transform: none;
}

html h1 { font-size: var(--glow-h1); }
html h2 { font-size: var(--glow-h2); line-height: 1.1; }
html h3 { font-size: var(--glow-h3); line-height: 1.25; letter-spacing: -0.005em; }
html h4 { font-size: var(--glow-t4); line-height: 1.3; }
/* h5/h6 had no Glow rule at any level — the next one written would have
   landed on whatever stylesheet happened to load. Closed 30 Aug 2026, B-06. */
html h5 { font-size: var(--glow-body); line-height: 1.35; }
html h6 { font-size: var(--glow-small); line-height: 1.4; }

html p {
  font-family: var(--glow-font-body);
  font-weight: 400;
  text-decoration: none;
}

/* Our own button must not inherit the theme's brown fill and 6px radius. */
html .glow-button {
  font-family: var(--glow-font-body);
  font-size: var(--glow-small);
  font-weight: 500;
  text-transform: none;
  color: var(--glow-cream);
  background-color: var(--glow-burgundy);
  border: 1px solid var(--glow-burgundy);
  border-radius: var(--glow-radius-ui);
  padding: 0.9rem 1.75rem;
  line-height: 1.2;
}

html .glow-button:hover,
html .glow-button:focus-visible {
  color: var(--glow-cream);
  background-color: var(--glow-deep);
  border-color: var(--glow-deep);
}

html .glow-button--outline {
  color: var(--glow-ink);
  background-color: transparent;
  border-color: var(--glow-border);
}

html .glow-button--outline:hover,
html .glow-button--outline:focus-visible {
  background-color: var(--glow-blush);
}

/* Form controls on /contact — the theme styles inputs too. */
html .glow-form input,
html .glow-form textarea {
  font-family: var(--glow-font-body);
  font-size: var(--glow-small);
}

/* ---- Membership forms ------------------------------------------------------
   CAUTION: HubL is parsed INSIDE CSS COMMENTS. This file is included into
   main.css, which HubSpot renders through Jinjava, so a HubL tag written in a
   comment here is still executed — naming the passwordless tag in braces failed
   the upload with "Unknown tag". Refer to HubL tags by name only, never in
   their delimiters, anywhere in this stylesheet.

   HubSpot's injected settings CSS paints a background on the <form> elements
   that the member_login and passwordless auth tags emit — measured at
   rgb(116,106,100), a grey-brown. Inside the cream auth card that renders as a
   slab behind the fields, and it makes the real <label> elements (which DO
   exist: "Email*", "Password*") invisible, because --glow-muted sits within a
   few points of it. It reads as "placeholders only, no labels" — the exact
   defect the rebuilt screens were meant to remove.

   The form is a layout element here, not a surface. Nothing about it should
   paint. Scoped under .glow-auth so no other HubSpot form is affected. */
html .glow-auth form,
html .glow-auth #hs-membership-form,
html .glow-auth #hs-membership-passwordless-auth-form {
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
}

/* asset-bundle touch 1785734133 — see notes: HubSpot regenerates template_main.min.css
   from main.css, and editing an INCLUDED partial does not always bump the
   bundle version. Editing main.css itself does. */

/* bundle touch 1785734258 */

/* bundle touch 1785735108 */

/* bundle touch 1785735630 */

/* bundle touch 1785736172 */

/* bundle touch 1785736254 */

/* bundle touch 1785736381 */

/* bundle touch 1785736860 */

/* bundle touch 1785736923 */

/* bundle touch 1785737180 */

/* bundle touch 1785737338 */

/* bundle touch 1785737483 */

/* bundle touch 1785737608 */

/* bundle touch 1785751291 */

/* bundle touch 1785751449 */

/* bundle touch 1785752566 */

/* bundle touch 1785752831 */

/* bundle bump: public scale tokens, 8 Aug 2026 */

/* bundle touch: one title size, 8 Aug 2026 */

/* bundle touch: one eyebrow, both surfaces, 8 Aug 2026 */

/* bundle touch: image slots, 8 Aug 2026 */

/* bundle touch: holding page, 8 Aug 2026 */

/* bundle touch: the health declaration form, 9 Aug 2026 */

/* bundle touch: not-connected notice on the declaration form, 10 Aug 2026 */

/* bundle touch: the updates invitation row, 10 Aug 2026 */

/* bundle touch: before-your-visit card on the overview, 10 Aug 2026 */

/* bundle touch: overview-card review harness, 10 Aug 2026 */

/* touched 13 Aug 2026 — journey component added to _portal.css */

/* touched — addendum spine fix */