/* ============================================================
   AminoVita business portal — shared primitives.

   Every /business/ page carried its own copy of these tokens and
   its own button rule. That produced seven different primary
   buttons and a --av-faint that no page could change without
   changing ten files. This is the single definition; page
   stylesheets are loaded after it and may still override, but
   should not restate.
   ============================================================ */

:root{
  --av-bg:#F9F9F6; --av-card:#FFFFFF; --av-sunk:#FBFBF7;
  --av-ink:#14161C; --av-muted:#565B66;

  /* 4.71:1 on --av-bg, 4.97:1 on --av-card. The previous #8A8F99
     was 3.08:1 — under the 4.5:1 AA floor, and it carried dates,
     SKUs and form labels, which are read, not decorative. */
  --av-faint:#6B707A;

  --av-line:#E6E3DC; --av-line-strong:#D8D4CB;

  /* Brand green. The B2B landing, login and set-password screens have
     always carried #0F6A54, and the signed-in portal carried no green
     at all — so an applicant saw the brand through the whole funnel and
     then lost it the moment the account opened. This is that same
     value, not a new one. 6.5:1 against white, so it holds button
     labels at AA.
     --av-sage is its tint, for support surfaces that need to read as a
     different kind of space without becoming another card. */
  --av-green-deep:#0F6A54; --av-green-deeper:#0B523F; --av-sage:#E4EDE6;

  /* Form fields. --av-sunk is a hair off white, which is right for a
     static panel but leaves an input reading as part of the sheet it
     sits on. A control you can type into needs to be visibly a well. */
  --av-field:#F4F3EE;

  /* Research areas, matching the retail filter chips on shop.html. The
     portal was missing --av-band-utility, which the retail pages have
     carried since the taxonomy landed. */
  --av-band-recovery:#1D9E75; --av-band-vitality:#2A72C3; --av-band-neuro:#6B4FCC;
  --av-band-utility:#6F6F6A;

  --st-paid:#0F7A4E;      --st-paid-bg:rgba(15,122,78,.10);
  --st-pending:#7A4A00;   --st-pending-bg:rgba(122,74,0,.10);
  --st-shipped:#164A85;   --st-shipped-bg:rgba(22,74,133,.10);
  --st-refunded:#B0271F;  --st-refunded-bg:rgba(176,39,31,.10);
  --st-delivered:#0F7A4E; --st-delivered-bg:rgba(15,122,78,.10);
  --st-closed:#6B7280;    --st-closed-bg:rgba(107,114,128,.12);

  --serif:'DM Serif Display',Georgia,serif;
  --sans:'DM Sans',system-ui,sans-serif;
  --mono:'SF Mono',Menlo,ui-monospace,monospace;
  --max:1180px;

  /* Card elevation. Tokens rather than a class because most of these
     surfaces are rendered by page JS, so the page's own rule is the only
     place to apply them — and seven hand-copied shadows is how the
     portal ended up with seven different cards in the first place.
     A sheet resting on the background: a top inset highlight for the lit
     edge, a tight shadow for contact, a wide soft one for the lift. */
  --av-shadow-card:inset 0 1px 0 rgba(255,255,255,.9),
                   0 1px 2px rgba(20,22,28,.035),
                   0 12px 28px -18px rgba(20,22,28,.16);
  --av-shadow-card-hover:inset 0 1px 0 rgba(255,255,255,.9),
                         0 1px 2px rgba(20,22,28,.035),
                         0 18px 36px -18px rgba(20,22,28,.22);
}

/* ---------- Numerals ----------
   Money, quantities and identifiers are data: they get sans with
   tabular figures so columns align and digits do not reflow as
   values change. Serif is reserved for headings and for the one
   grand total per screen — .num-hero below. Mixing the two was
   putting 19px serif prices next to 44px serif headings and
   flattening the hierarchy between them. */
.num{font-family:var(--sans);font-variant-numeric:tabular-nums;font-feature-settings:'tnum' 1;letter-spacing:-.005em;}
.num-sm{font-size:13px;font-weight:500;color:var(--av-muted);}
.num-md{font-size:15px;font-weight:600;color:var(--av-ink);}
.num-lg{font-size:19px;font-weight:600;color:var(--av-ink);letter-spacing:-.012em;}
.num-xl{font-size:30px;font-weight:600;color:var(--av-ink);letter-spacing:-.02em;line-height:1;}
/* The single statement figure on a screen. Serif earns its place
   here because nothing else nearby competes with it. */
.num-hero{font-family:var(--serif);font-weight:400;font-variant-numeric:tabular-nums;letter-spacing:-.01em;}

/* Order numbers, SKUs, tracking codes — read character by
   character, so monospace, but at text size rather than display
   size. */
.code{font-family:var(--mono);font-variant-numeric:tabular-nums;letter-spacing:.01em;}

/* ---------- Buttons ----------
   Three treatments, no more: filled, outlined, text. Size comes
   from .btn-lg / .btn-sm, never from a bespoke padding. Labels are
   sentence case — the uppercase letterspaced treatment is now
   reserved for section kickers, where it reads as a label rather
   than as something to click. */
.btn{
  font-family:inherit;font-size:14px;font-weight:600;line-height:1;
  letter-spacing:0;text-transform:none;
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  padding:13px 22px;min-height:44px;border-radius:999px;
  border:1px solid transparent;cursor:pointer;text-decoration:none;white-space:nowrap;
  transition:background .15s ease,border-color .15s ease,color .15s ease;
}
.btn:focus-visible{outline:2px solid var(--av-green-deep);outline-offset:2px;}
.btn svg{width:15px;height:15px;stroke-width:1.9;flex:none;}

/* Green, not ink. The filled button is the only thing on a portal
   screen that commits something, and it is the one place the brand
   should be unmistakable.
   State readouts still don't take this treatment, but they no longer
   take a near-black fill either: two ink slabs sat directly above the
   product list and outranked it, which is the opposite of what a
   filter should do. Selected now means a sage tint with green text —
   same distinction, a tenth of the weight. The ink fills that remain
   are the 18px cart count and the avatar, where a tint has nothing
   left to read against. */
.btn-primary{background:var(--av-green-deep);color:#fff;border-color:var(--av-green-deep);}
.btn-primary:hover:not(:disabled){background:var(--av-green-deeper);border-color:var(--av-green-deeper);}

.btn-secondary{background:var(--av-card);color:var(--av-ink);border-color:var(--av-line-strong);}
.btn-secondary:hover:not(:disabled){border-color:var(--av-ink);background:rgba(20,22,28,.04);}

/* The same pair flipped, for the one dark surface in the portal. It
   replaces a mint fill that existed nowhere else in the product. */
.btn-invert{background:#fff;color:var(--av-ink);border-color:#fff;}
.btn-invert:hover:not(:disabled){background:#EFEEE8;border-color:#EFEEE8;}

/* Text actions sit in table rows and card footers where a bordered
   control would add a third box. Underline is permanent — an
   underline that only appears on hover is not discoverable. */
.btn-link{
  background:none;border:0;padding:0;min-height:0;border-radius:0;
  font-family:inherit;font-size:13.5px;font-weight:600;color:var(--av-ink);
  cursor:pointer;text-decoration:none;
  border-bottom:1px solid var(--av-line-strong);padding-bottom:2px;
}
.btn-link:hover:not(:disabled){border-bottom-color:var(--av-ink);}

.btn-lg{font-size:15px;padding:16px 28px;min-height:52px;}
.btn-sm{font-size:13px;padding:9px 16px;min-height:36px;}
.btn:disabled{opacity:.45;cursor:not-allowed;}

/* ---------- Section label ----------
   The one place uppercase letterspacing survives. */
.kicker{
  font-family:var(--sans);font-size:10.5px;font-weight:700;
  letter-spacing:.22em;text-transform:uppercase;color:var(--av-faint);
  display:inline-flex;align-items:center;gap:10px;
}
.kicker::before{content:'';width:22px;height:1px;background:var(--av-line-strong);}

/* ---------- Status pill ---------- */
.st-pill{display:inline-block;font-size:10px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;padding:4px 10px;border-radius:999px;}
.pill-paid{color:var(--st-paid);background:var(--st-paid-bg);}
.pill-pending{color:var(--st-pending);background:var(--st-pending-bg);}
.pill-shipped{color:var(--st-shipped);background:var(--st-shipped-bg);}
.pill-refunded{color:var(--st-refunded);background:var(--st-refunded-bg);}
.pill-delivered{color:var(--st-delivered);background:var(--st-delivered-bg);}
.pill-cancelled,.pill-expired,.pill-closed{color:var(--st-closed);background:var(--st-closed-bg);}

/* ---------- Glass panel ----------
   The house hero treatment. It lived inline on the dashboard and on no
   other signed-in screen, so the B2B landing page out-classed the
   product it was selling. This is the definition for all of /business/.

   The landing (business/index.html) does NOT load this sheet — it is a
   public page and pulling the whole portal stylesheet onto it to share
   one panel is a bad trade — so it keeps its own copy of the formula.
   Two copies, deliberately. Change the numbers here and they must be
   changed there too, or the account will stop matching the pitch.

   Three stacked layers, and all three are load-bearing: saturated
   colour blobs on z-0, a frosted pane over them on z-1 that actually
   blurs what is underneath, content on z-2. backdrop-filter with
   nothing behind it is just a flat tint, which is why the blobs exist.

   Panels are contained — a glass panel must never run to the browser
   edge. Put it inside .container. */
.glass-panel{position:relative;isolation:isolate;overflow:hidden;
  border-radius:24px;border:1px solid rgba(20,22,28,.07);
  background:linear-gradient(135deg,#FFFFFF 0%,#F2F7F4 100%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.95),
             0 1px 2px rgba(20,22,28,.04),
             0 24px 52px -28px rgba(20,22,28,.24);}

/* inset:-20% is the headroom that lets the blobs drift without ever
   exposing a hard edge inside the panel. 38s is long enough that the
   motion is never the thing you notice — it just stops reading as a
   still photograph of liquid. */
.glass-panel::before{content:'';position:absolute;inset:-20%;z-index:0;pointer-events:none;
  background:
    radial-gradient(42% 58% at 78% 18%,rgba(29,158,117,.46) 0%,transparent 70%),
    radial-gradient(38% 50% at 96% 72%,rgba(42,114,195,.22) 0%,transparent 72%),
    radial-gradient(34% 44% at 12% 96%,rgba(107,79,204,.13) 0%,transparent 72%);
  animation:av-glass-drift 38s ease-in-out infinite;will-change:transform;}
@keyframes av-glass-drift{
  0%,100%{transform:translate3d(0,0,0) scale(1);}
  34%    {transform:translate3d(2.6%,-2.2%,0) scale(1.07);}
  67%    {transform:translate3d(-2.2%,2.4%,0) scale(1.04);}
}

/* Two gradients on one layer: a white veil for legibility, plus a
   single diagonal specular sweep. The sweep is what makes the surface
   read as a pane rather than as a wash — it was the one layer the
   portal copy was missing. */
.glass-panel::after{content:'';position:absolute;inset:0;z-index:1;pointer-events:none;
  -webkit-backdrop-filter:blur(44px) saturate(170%);backdrop-filter:blur(44px) saturate(170%);
  background:
    linear-gradient(116deg,transparent 19%,rgba(255,255,255,.40) 33%,transparent 45%),
    linear-gradient(135deg,rgba(255,255,255,.76) 0%,rgba(255,255,255,.58) 52%,rgba(255,255,255,.68) 100%);}

/* Content sits above all three layers. Anything placed directly in a
   .glass-panel needs this or it renders under the frosted pane. */
.glass-panel > *{position:relative;z-index:3;}

/* Grain. Real glass is never perfectly smooth, and a flawless gradient
   is what reads as synthetic — a few percent of fractal noise is the
   whole difference. Sits above the pane, below the content.
   .08 is measured, not guessed: below ~.06 the noise is invisible on a
   2x display, above ~.10 it starts muddying the green. */
.glass-grain{position:absolute;inset:0;z-index:2;pointer-events:none;opacity:.08;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size:200px 200px;}

/* Without backdrop-filter the pane would sit opaque over the blobs and
   kill the colour entirely, so drop the blobs and fall back to a plain
   wash. Ship this — it is the difference between "no glass" and "a grey
   box" on older Firefox. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .glass-panel::before{display:none;}
  .glass-panel::after{background:linear-gradient(135deg,rgba(255,255,255,0) 0%,rgba(29,158,117,.10) 100%);}
}

/* ---------- Page hero ----------
   The four inner screens opened with a bare kicker and an h1 sitting
   directly on --av-bg, so they read as documents while the dashboard
   read as software. This gives them the same opening material at a
   shorter height, since they have no primary action to host. */
.page-hero{padding:34px 36px 32px;margin-bottom:30px;}
.page-hero .kicker{margin-bottom:14px;}

/* The base .glass-panel blobs are aimed at the dashboard: a tall panel
   with a CTA on the right, so the green sits at 78% 18% and lands
   behind something. A .page-hero is half that height with all its
   content on the left, so the same blob pooled in the empty right-hand
   third and the colour read as a stray wash rather than as the surface
   the heading is printed on.

   Scoped to .page-hero on purpose — .glass-panel itself is mirrored by
   hand in business/index.html (see the note on that rule), and
   overriding the base here would silently desync the landing page from
   the portal. Nothing below touches the dashboard or the landing. */
.page-hero.glass-panel::before{
  background:
    radial-gradient(62% 150% at 20% 0%,rgba(29,158,117,.40) 0%,transparent 68%),
    radial-gradient(46% 120% at 88% 110%,rgba(15,106,84,.26) 0%,transparent 70%),
    radial-gradient(30% 80% at 60% 10%,rgba(42,114,195,.10) 0%,transparent 72%);
}
/* Shorter panel, so the 52px drop shadow that anchors a tall dashboard
   card reads as a lift the hero has not earned. */
.page-hero.glass-panel{box-shadow:inset 0 1px 0 rgba(255,255,255,.95),
  0 1px 2px rgba(20,22,28,.04),
  0 14px 34px -26px rgba(20,22,28,.20);}
/* 28px, not the 38px marketing size these opened at. Every consumer is
   a working screen reached from inside the portal, and a title that
   outweighs the table or form below it is backwards. The gate screens
   (login, set-password) are not .page-hero and keep their large type. */
.page-hero h1{font-family:var(--serif);font-size:28px;font-weight:400;letter-spacing:-.02em;line-height:1.1;margin:0;color:var(--av-ink);}
.page-hero p{font-size:15px;color:var(--av-muted);margin:10px 0 0;max-width:56ch;line-height:1.6;}
/* Trailing controls — the catalog's channel toggle, the orders count.
   Baseline-aligned against the h1 rather than centred, so a tall
   heading does not drag them down the panel. */
.page-hero-row{display:flex;align-items:flex-end;justify-content:space-between;gap:24px;flex-wrap:wrap;}
/* Dot-separated facts under the heading — channel, counts, pricing
   basis. These were pill badges on two screens, which put the loudest
   type on the page on facts nobody acts on. Quiet line, ink only on
   the figure that varies. */
.hero-meta{font-size:13px;color:var(--av-muted);display:flex;gap:9px;flex-wrap:wrap;margin-top:9px;}
.hero-meta b{color:var(--av-ink);font-weight:600;}
.hero-meta span:not(:last-child)::after{content:' ·';color:var(--av-line-strong);margin-left:9px;}

/* ---------- Micro label ----------
   Column headers, sidebar block titles and form field labels — one
   token for all three. Every screen re-declared this at its own size
   and tracking (10/.13, 10/.16, 10.5/.22, 11/.11), so the same kind of
   label read as a different element on each page, and a column header
   never lined up with the field label directly across from it. */
.col-label{font-size:11px;font-weight:700;letter-spacing:.11em;text-transform:uppercase;
  color:var(--av-faint);}
.col-label.r{text-align:right;}
.col-label.c{text-align:center;}

/* ---------- Money ----------
   Figures only compare when their decimal points stack, so anything
   that renders a currency amount in a column is right-aligned and
   tabular. The dashboard's order list was left-aligned proportional,
   which is why $3,600.00 and $540.00 read as unrelated numbers. */
.num{text-align:right;font-variant-numeric:tabular-nums;}

/* ---------- Segmented control ----------
   Reports which view you are looking at rather than performing an
   action, so it never takes .btn treatment. The catalog's channel
   switch and the order list's status filter are the same control and
   now share one definition. */
.seg{display:inline-flex;flex-wrap:wrap;background:#fff;border:1px solid var(--av-line);
  border-radius:999px;padding:4px;gap:2px;box-shadow:0 1px 2px rgba(0,0,0,.02);}
.seg button{background:transparent;border:0;padding:8px 22px;font-family:inherit;font-size:12.5px;
  font-weight:600;letter-spacing:.02em;color:var(--av-muted);cursor:pointer;border-radius:999px;
  transition:background 120ms ease,color 120ms ease;}
.seg button:hover:not(.active){color:var(--av-ink);}
.seg button.active{background:var(--av-sage);color:var(--av-green-deep);
  box-shadow:inset 0 0 0 1px rgba(15,106,84,.16);}
/* The order filter's four segments overflow 390px at the desktop
   padding and wrap, which strands the last one on a second row inside
   the pill. Tighter sides fit all four on one line; wrap stays on as
   the floor for anything longer. */
@media(max-width:560px){
  .seg button{padding:8px 13px;font-size:12px;}
}

/* ---------- Section heading ----------
   The rule under a heading has to outrank the hairlines between the
   rows beneath it, or the heading reads as just another row boundary.
   --av-line-strong is one shade off --av-line and was never going to
   carry that job, hence the explicit alpha.

   .32 was set when a section heading opened its own card and had one
   rule on screen to outrank. Pages now stack several sections inside a
   single sheet, and six near-black rules down one panel is what makes a
   settings page read as a printed form. .17 still beats the --av-line
   hairlines between rows, which is the only job the rule has. */
.section-head{display:flex;justify-content:space-between;align-items:baseline;gap:16px;
  margin-bottom:16px;padding-bottom:11px;border-bottom:1px solid rgba(20,22,28,.17);}
.section-head h2{font-family:var(--serif);font-weight:400;font-size:23px;letter-spacing:-.015em;
  line-height:1.1;margin:0;color:var(--av-ink);}
.section-head p{margin:0;font-size:13px;color:var(--av-muted);}
/* Narrow-column variant — the dashboard's ledger rail, the account
   screen's read-only column. 23px serif in a 340px column is a
   headline, not a section label. Pages keep their own margin. */
.section-head.rail h2{font-size:17px;}
@media (max-width:760px){
  .section-head{flex-direction:column;align-items:flex-start;gap:5px;}
}

/* ---------- Surface ----------
   Cards were #fff with a 0.5px border and no shadow, which is why a
   contact form carried the same visual weight as a reorder hero.
   For hand-written markup; page rules on JS-rendered cards use
   --av-shadow-card directly. */
.surface{background:var(--av-card);border:1px solid var(--av-line);border-radius:16px;
  box-shadow:var(--av-shadow-card);}
/* For cards that are also links or buttons. The lift is small on
   purpose: these appear four across, and a big hover translate in a
   grid reads as a row of buttons jumping. */
.surface-interactive{transition:transform .15s ease,box-shadow .15s ease,border-color .15s ease;}
.surface-interactive:hover{transform:translateY(-2px);border-color:var(--av-line-strong);
  box-shadow:var(--av-shadow-card-hover);}

/* ---------- Portal navigation ----------
   One definition for all seven signed-in screens. Previously each page
   carried its own copy, which is how the cart ended up on two of them
   and the wordmark linked to the dashboard on some and nowhere on
   others. Markup is rendered by ../js/av-portal-nav.js.

   A left rail rather than a top bar. The portal is a tool rather than a
   set of pages to read through — a buyer crosses between catalog, cart
   and orders several times inside one order — and a rail holds every
   destination on screen while they do it, instead of a strip that has
   to compete with the page heading for the top of the screen.

   The rail is dark because it is the one piece of furniture that is not
   content: a white rail against white cards needs a border to exist at
   all, and the eye then has to work out each time whether it is looking
   at navigation or at a panel. Green rather than neutral because the
   applicant has seen #0F6A54 through the whole funnel. */
.nav.rail{position:static;background:none;border:0;}

/* The two widths and the one place they are written down. Both the rail
   and the content offset read these, so they cannot drift apart and
   leave a gap or an overlap. */
:root{--rail-w:244px; --rail-mini-w:74px;}
html[data-rail=mini]{--rail-w:var(--rail-mini-w);}

.rail-pane{position:fixed;left:0;top:0;bottom:0;width:var(--rail-w);z-index:40;
  display:flex;flex-direction:column;
  background:linear-gradient(176deg,#13755D 0%,var(--av-green-deeper) 56%,#083B2E 100%);
  /* An inner right-edge highlight so the rail reads as a lit surface
     with an edge rather than a block of colour butted against the page,
     plus a soft cast onto the background for the same reason a card has
     one. */
  box-shadow:inset -1px 0 0 rgba(255,255,255,.10), 10px 0 34px -22px rgba(8,45,35,.55);
  transition:width 180ms cubic-bezier(.4,0,.2,1);}

/* The content moves with the rail. padding rather than margin, with the
   max-width widened by the same amount, so the text column stays
   centred in the space that is left instead of being pushed off-centre
   by a fixed left margin. .container is the only sibling the nav has on
   every portal page, which is what makes one rule enough.

   Desktop only. This beats every page's own .container rule on
   specificity, including their mobile ones, so left unscoped it would
   also be deciding the phone padding for seven pages that already have
   opinions about it.

   The rail side gets a somewhat wider gutter than the window edge, since
   the two are not comparable: the window edge is nothing, and the rail is
   a dark slab. But the margin only needs to be slight. This was 64px
   rising to 96px above 1200, on the theory that a heavy neighbour needs
   double the clearance; on a 1440 laptop — under the max-width, so not
   yet centring — that rendered as 96 against the rail and 32 against the
   window, and the band of empty floor read as a gap in the layout rather
   than breathing room. 56 is enough to keep the glass panel's soft edge
   off the slab.
   The max-width runs 24px under the gutter so the text column holds the
   same width it always had; only its position relative to the rail
   changes. Above the max-width the leftover splits evenly and both gaps
   grow on their own, so 56 is the worst case, not the typical one. */
@media(min-width:901px){
  .nav.rail ~ .container{max-width:calc(var(--max) + var(--rail-w) + 24px);
    padding-left:calc(56px + var(--rail-w));
    transition:max-width 180ms cubic-bezier(.4,0,.2,1),padding-left 180ms cubic-bezier(.4,0,.2,1);}
}

.rail-head{display:flex;align-items:center;gap:8px;padding:20px 14px 18px 20px;}
/* The "AminoVita Business" lockup: the name in the serif, "Business" in its
   italic. The .wm selector carries login and set-password, which mark the
   lockup up by hand and sit on white, so the green stays the default and
   the rail overrides it. Targeting em rather than span keeps
   invoice.html's "AminoVita Research" head out of it. */
.nav-wm{font-family:var(--serif);font-size:20px;color:var(--av-ink);text-decoration:none;white-space:nowrap;}
.nav-wm em,.wm em{font-style:italic;font-weight:400;letter-spacing:.005em;color:var(--av-green-deep);margin-left:6px;}
/* Scoped to the pane, not the whole rail: the phone bar carries the
   same lockup on white, where reversing it out leaves "AminoVita"
   invisible and the italic floating on its own. */
.rail-pane .nav-wm{color:#fff;}
/* Mint, not --av-green-deep: the pane is that green, so the brand
   italic would disappear into its own background. */
.rail-pane .nav-wm em{color:#8FD3B8;}

.rail-toggle{margin-left:auto;flex:none;width:30px;height:30px;border-radius:8px;
  display:inline-flex;align-items:center;justify-content:center;position:relative;
  background:none;border:0;padding:0;cursor:pointer;color:rgba(255,255,255,.62);
  transition:background 140ms ease,color 140ms ease;}
.rail-toggle svg{width:17px;height:17px;transition:transform 180ms cubic-bezier(.4,0,.2,1);}
.rail-toggle:hover{background:rgba(255,255,255,.12);color:#fff;}
.rail-toggle:focus-visible{outline:2px solid #8FD3B8;outline-offset:2px;}

.rail-nav{display:flex;flex-direction:column;gap:2px;padding:6px 12px;}
.rail-link{position:relative;display:flex;align-items:center;gap:12px;
  padding:9px 12px;border-radius:10px;text-decoration:none;
  font-size:13.5px;font-weight:500;color:rgba(255,255,255,.74);
  transition:background 140ms ease,color 140ms ease;}
.rail-ico{flex:none;width:20px;height:20px;display:inline-flex;align-items:center;
  justify-content:center;position:relative;}
.rail-ico svg{width:18px;height:18px;display:block;}
.rail-lbl{white-space:nowrap;overflow:hidden;}
.rail-link:hover{background:rgba(255,255,255,.10);color:#fff;}
.rail-link:focus-visible{outline:2px solid #8FD3B8;outline-offset:-2px;}
/* Where you are is a lit pane; where you could go is text on the rail.
   The hairline above and the inset below are the same glass treatment
   the page's own panels use, at rail scale. */
.rail-link.active{background:rgba(255,255,255,.145);color:#fff;font-weight:600;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.22), 0 1px 2px rgba(6,38,29,.22);}

/* Collapsed tooltip. Only thing naming the destination once the labels
   are gone, so it is a real panel rather than a native title delay. */
.rail-tip{position:absolute;left:calc(100% + 10px);top:50%;transform:translateY(-50%) translateX(-4px);
  background:var(--av-ink);color:#fff;font-size:12px;font-weight:500;letter-spacing:.005em;
  padding:6px 10px;border-radius:7px;white-space:nowrap;pointer-events:none;
  opacity:0;visibility:hidden;z-index:50;box-shadow:0 6px 18px rgba(20,22,28,.22);
  transition:opacity 120ms ease,transform 120ms ease,visibility 120ms;}
html[data-rail=mini] .rail-link:hover .rail-tip,
html[data-rail=mini] .rail-link:focus-visible .rail-tip,
html[data-rail=mini] .rail-toggle:hover .rail-tip,
html[data-rail=mini] .rail-toggle:focus-visible .rail-tip{
  opacity:1;visibility:visible;transform:translateY(-50%) translateX(0);}

.rail-foot{margin-top:auto;padding:12px;display:flex;flex-direction:column;gap:2px;
  border-top:1px solid rgba(255,255,255,.10);}

/* Cart. The badge is the count when there is room for it and a dot when
   there is not — a number pinned to an 18px glyph in a 74px rail is
   unreadable, and an empty space is indistinguishable from an empty
   cart. */
.rail-cart .nav-cart-count{margin-left:auto;background:#8FD3B8;color:var(--av-green-deeper);
  font-family:var(--sans);font-size:10.5px;font-weight:700;border-radius:999px;
  min-width:19px;height:19px;padding:0 6px;display:inline-flex;align-items:center;
  justify-content:center;line-height:1;letter-spacing:.02em;flex:none;}
.nav-cart-count.zero{display:none;}
.rail-dot{position:absolute;top:-1px;right:-2px;width:7px;height:7px;border-radius:50%;
  background:#8FD3B8;box-shadow:0 0 0 2px var(--av-green-deeper);display:none;}
.rail-dot.zero{display:none;}

/* Account menu. The email and sign-out used to sit loose in the bar, so
   a destructive action had the same weight as the user's own address. */
.acct{position:relative;}
.acct-btn{display:flex;width:100%;align-items:center;gap:10px;font-family:inherit;
  color:#fff;background:none;border:1px solid transparent;border-radius:10px;
  padding:9px 10px;cursor:pointer;text-align:left;
  transition:background 140ms ease;}
.acct-btn:hover,.acct-btn[aria-expanded=true]{background:rgba(255,255,255,.10);}
.acct-btn:focus-visible{outline:2px solid #8FD3B8;outline-offset:-2px;}
.acct-avatar{width:30px;height:30px;border-radius:9px;background:rgba(255,255,255,.16);
  color:#fff;font-size:11.5px;font-weight:700;display:inline-flex;align-items:center;
  justify-content:center;letter-spacing:.02em;flex:none;text-transform:uppercase;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.18);}
/* min-width:0 so a long company name ellipses instead of forcing the
   button wider than the rail. */
.acct-id{display:flex;flex-direction:column;min-width:0;line-height:1.25;}
.acct-id .co{font-size:12.5px;font-weight:600;color:#fff;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;}
.acct-id .em{font-size:11px;color:rgba(255,255,255,.58);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;margin-top:1px;}
.acct-chev{width:13px;height:13px;stroke-width:2.2;flex:none;margin-left:auto;
  color:rgba(255,255,255,.55);}
/* Opens upward: the button is the last thing in the rail, so a menu
   hanging below it would land off the bottom of the screen. */
.acct-menu{position:absolute;left:8px;right:8px;bottom:calc(100% + 8px);min-width:216px;
  background:var(--av-card);border:1px solid var(--av-line);border-radius:12px;
  box-shadow:0 14px 38px rgba(8,45,35,.26);padding:6px;display:none;z-index:50;}
.acct-menu[data-open]{display:block;}
.acct-who{padding:10px 12px 11px;border-bottom:1px solid var(--av-line);margin-bottom:6px;}
.acct-who .co{font-size:13px;font-weight:600;color:var(--av-ink);line-height:1.3;}
.acct-who .em{font-size:12px;color:var(--av-faint);margin-top:3px;word-break:break-all;line-height:1.35;}
.acct-item{display:block;width:100%;text-align:left;font-family:inherit;font-size:13px;font-weight:500;color:var(--av-ink);background:none;border:0;border-radius:8px;padding:9px 12px;cursor:pointer;text-decoration:none;}
.acct-item:hover{background:rgba(20,22,28,.05);}
.acct-item.danger{color:var(--st-refunded);}

/* ---------- Collapsed ----------
   Centre the glyphs and take the words out of the flow entirely.
   Keeping the labels and clipping them left them half-visible against
   the rail edge mid-transition. */
html[data-rail=mini] .rail-head{padding:20px 0 18px;justify-content:center;}
html[data-rail=mini] .rail-head .nav-wm{display:none;}
html[data-rail=mini] .rail-toggle{margin-left:0;}
html[data-rail=mini] .rail-toggle svg{transform:rotate(180deg);}
html[data-rail=mini] .rail-nav,
html[data-rail=mini] .rail-foot{padding-left:10px;padding-right:10px;}
html[data-rail=mini] .rail-link,
html[data-rail=mini] .acct-btn{justify-content:center;gap:0;padding-left:0;padding-right:0;}
html[data-rail=mini] .rail-lbl,
html[data-rail=mini] .acct-id,
html[data-rail=mini] .acct-chev,
html[data-rail=mini] .rail-cart .nav-cart-count{display:none;}
html[data-rail=mini] .rail-dot:not(.zero){display:block;}
/* Released from the button's width so it can be as wide as the name. */
html[data-rail=mini] .acct-menu{left:calc(100% + 10px);right:auto;bottom:4px;width:244px;}

/* The phone header. Hidden on a laptop, where the rail is the header. */
.rail-bar{display:none;}
.rail-scrim{display:none;}

@media(max-width:900px){
  .page-hero{padding:26px 22px 24px;margin-bottom:22px;border-radius:20px;}
  .page-hero h1{font-size:24px;}
  .page-hero-row{align-items:flex-start;flex-direction:column;gap:16px;}

  /* A phone has no room for a rail of either width, so it goes
     off-canvas behind a burger. Always full width when it does arrive:
     collapsing is a laptop trade of labels for catalog width, and a
     drawer that covers the page has no such trade to make. The content
     keeps whatever padding its own page gave it. */
  .rail-bar{display:flex;align-items:center;gap:10px;position:sticky;top:0;z-index:30;
    padding:10px 16px;background:rgba(255,255,255,.88);
    -webkit-backdrop-filter:saturate(150%) blur(12px);backdrop-filter:saturate(150%) blur(12px);
    border-bottom:1px solid var(--av-line);}
  .rail-burger{width:38px;height:38px;flex:none;display:inline-flex;align-items:center;
    justify-content:center;border-radius:10px;border:1px solid var(--av-line);
    background:#fff;color:var(--av-ink);cursor:pointer;padding:0;}
  .rail-burger svg{width:19px;height:19px;}
  .rail-burger:focus-visible{outline:2px solid var(--av-green-deep);outline-offset:2px;}
  .rail-bar .nav-wm{font-size:18px;}
  .bar-cart{margin-left:auto;position:relative;width:38px;height:38px;flex:none;
    display:inline-flex;align-items:center;justify-content:center;border-radius:10px;
    color:var(--av-ink);text-decoration:none;}
  .bar-cart svg{width:19px;height:19px;}
  .bar-cart:focus-visible{outline:2px solid var(--av-green-deep);outline-offset:2px;}
  /* Pinned to the glyph rather than sat beside it: the bar is 38px tall
     and a badge in flow pushes the icon off centre. */
  .bar-cart .nav-cart-count{position:absolute;top:1px;right:-1px;
    background:var(--av-green-deep);color:#fff;font-family:var(--sans);font-size:10px;
    font-weight:700;border-radius:999px;min-width:17px;height:17px;padding:0 5px;
    display:inline-flex;align-items:center;justify-content:center;line-height:1;
    letter-spacing:.02em;box-shadow:0 0 0 2px #fff;}
  /* Restated because the rule above matches .zero just as specifically
     and sits later in the file, so the shared hide rule loses and an
     empty cart was badged "0". */
  .bar-cart .nav-cart-count.zero{display:none;}

  .rail-pane{width:272px;transform:translateX(-100%);
    transition:transform 220ms cubic-bezier(.4,0,.2,1);box-shadow:none;}
  html[data-rail-open] .rail-pane{transform:translateX(0);
    box-shadow:18px 0 44px -18px rgba(8,45,35,.5);}
  /* The drawer always shows labels, whatever the laptop left behind. */
  html[data-rail=mini] .rail-head{padding:20px 14px 18px 20px;justify-content:flex-start;}
  html[data-rail=mini] .rail-head .nav-wm{display:block;}
  html[data-rail=mini] .rail-toggle{display:none;}
  html[data-rail=mini] .rail-nav,
  html[data-rail=mini] .rail-foot{padding-left:12px;padding-right:12px;}
  html[data-rail=mini] .rail-link,
  html[data-rail=mini] .acct-btn{justify-content:flex-start;gap:12px;padding-left:12px;padding-right:12px;}
  html[data-rail=mini] .acct-btn{padding-left:10px;padding-right:10px;}
  html[data-rail=mini] .rail-lbl,
  html[data-rail=mini] .acct-id{display:block;}
  html[data-rail=mini] .acct-id{display:flex;}
  html[data-rail=mini] .acct-chev,
  html[data-rail=mini] .rail-cart .nav-cart-count:not(.zero){display:inline-flex;}
  html[data-rail=mini] .rail-dot{display:none;}
  html[data-rail=mini] .acct-menu{left:8px;right:8px;bottom:calc(100% + 8px);width:auto;}
  .rail-toggle{display:none;}
  .rail-tip{display:none;}

  .rail-scrim{display:block;position:fixed;inset:0;z-index:35;
    background:rgba(8,45,35,.42);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);}
  .rail-scrim[hidden]{display:none;}
}

/* ---------- Legal footer ----------
   Injected by av-portal-nav.js. Carries .container as well, so it picks
   up each page's own container geometry and the rail offset above, and
   lands flush with the page body rather than under the rail.

   Deliberately quiet: it is a reference, not a destination. A rule does
   the separating, so it reads as the end of the page without a filled
   band — the portal separates sections without dark bands by design.

   That rule sits on the link row, not on .portal-foot. The footer
   carries .container, whose border box runs the full width of the
   window, so a border there reaches the browser edge — and nothing here
   bleeds to the edge. On the content box it stops where the page
   content stops.

   Sticky-footer layout: the host is #portal on the rail pages and <body>
   on checkout/profile, and av-portal-nav.js tags whichever it is.
   margin-top:auto eats the leftover column space, so on a short page —
   an account with no orders yet — the footer lands on the bottom edge
   instead of stranding mid-screen with blank page beneath it. On a tall
   page it just follows the content as before.

   This is why those pages clear the inline display instead of setting
   'block': an inline display would beat the flex declared here. */
.portal-foot-host{display:flex;flex-direction:column;min-height:100vh;}
.portal-foot-host > .portal-foot{margin-top:auto;}
/* Flex items with `margin:0 auto` are sized to fit-content, not to
   max-width — so once the host became a flex column, every .container
   stopped obeying --max and started taking the width of whatever it
   happened to hold. At 1440 that put the page title at x=401 on the
   dashboard, 421 on the catalog, 442 on contact and 300 on the order,
   and the whole frame jumped as you moved through the rail. width:100%
   restores the intended behaviour: fill, then clamp at max-width. */
.portal-foot-host > .container{width:100%;}

.portal-foot{margin-top:52px;padding:0 32px 40px;}
.portal-foot-links{display:flex;flex-wrap:wrap;gap:6px 20px;
  border-top:1px solid var(--av-line);padding-top:22px;margin-bottom:10px;}
.portal-foot-links a{font-size:12.5px;color:var(--av-muted);text-decoration:none;
  border-bottom:1px solid transparent;}
.portal-foot-links a:hover{color:var(--av-ink);border-bottom-color:var(--av-ink);}
.portal-foot-links a:focus-visible{outline:2px solid var(--av-green-deep);outline-offset:2px;}
.portal-foot-fine{margin:0;font-size:11.5px;line-height:1.7;color:var(--av-faint);
  max-width:620px;}
.portal-foot-fine a{color:var(--av-faint);}
.portal-foot-fine a:hover{color:var(--av-ink);}

@media(max-width:900px){
  .portal-foot{padding:20px 16px 34px;margin-top:40px;}
}

@media print{
  .nav,.rail-pane,.rail-bar,.rail-scrim{display:none;}
  .nav.rail ~ .container{max-width:var(--max);padding-left:32px;}
  /* A printed order or invoice is a filed copy, not a web page — the
     link list prints as dead text. The fine print stays: it is the
     research-use statement. */
  .portal-foot-links{display:none;}
  .portal-foot{margin-top:24px;}
  /* The blobs and the frosted pane cost a page of toner and print as a
     grey smear. Orders and invoices do get printed. */
  .glass-panel{box-shadow:none;border-color:var(--av-line);background:#fff;}
  .glass-panel::before,.glass-panel::after,.glass-grain{display:none;}
  .surface{box-shadow:none;}
}

@media(prefers-reduced-motion:reduce){
  .btn{transition:none;}
  .rail-pane,.nav.rail ~ .container,.rail-toggle svg{transition:none;}
  /* The blobs stay — they are what makes the glass read as glass. Only
     the drift stops. */
  .glass-panel::before{animation:none;}
  .surface-interactive{transition:none;}
  .surface-interactive:hover{transform:none;}
}
