/* The one dark surface in the portal, and the nav row that sticks under it.
   CodePlus puts brand and account on the dark strip and navigation on the pale
   row below, so the two never compete.

   Both rows are rendered by includes/portal_chrome.php — one partial, three
   call sites (the two shop pages and portal_header.php). The markup used to be
   pasted into each of the three and had already drifted; the selectors here
   assume the partial's structure, so a fourth hand-written copy will not pick
   up the menus or the sticky behaviour. */
.pt-top {
  /* Two stops rather than a flat fill: at 68px a single colour reads as a slab.
     The pair is close enough that it is felt as depth rather than seen as a
     gradient. */
  background: linear-gradient(180deg, var(--topbar-bg) 0%, var(--topbar-bg2) 100%);
  color: var(--on-dark);
  border-block-end: 1px solid var(--on-dark-06);
  position: relative;
  z-index: 120;
}
.pt-top__inner,
.pt-nav__inner {
  max-width: 78rem; margin: 0 auto; padding: 0 20px;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
}
.pt-top__inner { min-height: 68px; }
.pt-top__brand {
  display: inline-flex; align-items: center; gap: 10px;
  color: var(--on-dark); text-decoration: none;
  font-weight: 600; font-size: 15.5px; letter-spacing: -0.01em;
}
.pt-top__brand img { height: 24px; width: auto; }
/* The fused logo (icon + wordmark, one line) reads as a wordmark rather
   than an icon, so it earns more height than the bare icon mark did — the
   68px bar has the room. Its narrower stand-in for the width-constrained
   mobile bar (below) stays hidden until that breakpoint asks for it. */
.pt-top__brand img.pt-top__logo { height: 34px; }
.pt-top__brand img.pt-top__logo-compact { display: none; }
.pt-top__right { display: flex; align-items: center; gap: 10px; font-size: 13.5px; }

/* The cart icon, shown on every page that renders this chrome (including the
   dashboard, where it is fixed at zero — see portal_chrome()'s docblock). The
   badge reuses --danger/--on-danger rather than a colour of its own: the same
   pairing already renders on portal.css's danger stat tile and danger border,
   and probe_portal_theme.php fails on any literal written here regardless. */
.pt-cart { position: relative; display: inline-flex; align-items: center; color: var(--on-dark-70); padding: 6px; border-radius: var(--r); transition: color .18s ease; }
.pt-cart:hover { color: var(--on-dark); }
.pt-cart__qty {
  position: absolute; top: -2px; inset-inline-end: -2px;
  min-width: 17px; height: 17px; padding: 0 4px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--danger); color: var(--on-danger);
  border-radius: 999px; font-size: 10.5px; font-weight: 600; line-height: 1;
}

/* Guest: a quiet text link beside a bordered one. Two links of equal weight
   made the strip read as a choice between two unrelated things; the border
   says which one the page expects most visitors to want. */
.pt-top__link {
  color: var(--on-dark-70); text-decoration: none; font-weight: 500;
  padding: 8px 4px; transition: color .18s ease;
}
.pt-top__link:hover { color: var(--on-dark); }
.pt-top__signin {
  color: var(--on-dark); text-decoration: none; font-weight: 600;
  padding: 8px 16px; border-radius: 999px;
  border: 1px solid var(--on-dark-14);
  transition: background-color .18s ease, border-color .18s ease;
}
.pt-top__signin:hover { background: var(--on-dark-10); border-color: var(--on-dark-35); }

/* ── The account button and its menu ─────────────────────────────────── */
.pt-acct { position: relative; }
.pt-acct__btn {
  display: flex; align-items: center; gap: 9px;
  background: transparent; border: 1px solid transparent; cursor: pointer;
  color: var(--on-dark); font: inherit; font-size: 13.5px;
  padding: 5px 8px 5px 5px; border-radius: 999px;
  transition: background-color .18s ease, border-color .18s ease;
}
.pt-acct__btn:hover { background: var(--on-dark-10); border-color: var(--on-dark-14); }
.pt-acct__avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%; flex: none;
  background: var(--on-dark-14); color: var(--on-dark);
  font-size: 11.5px; font-weight: 600; letter-spacing: .02em;
}
/* The name is the widest thing on the strip and the least load-bearing — the
   avatar already identifies the account. It goes first when space runs out. */
.pt-acct__name {
  font-weight: 500; max-width: 15ch;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pt-acct__caret { opacity: .65; transition: transform .2s ease; flex: none; }
.pt-acct__btn[aria-expanded="true"] .pt-acct__caret { transform: rotate(180deg); }

/* One card shape for both menus. Hidden with visibility rather than display so
   the open/close transition has something to animate — visibility:hidden also
   takes the closed card out of hit-testing, so it cannot swallow a click meant
   for the page underneath the way an opacity:0 card would. */
.pt-menu, .pt-nav__panel {
  position: absolute; inset-inline-end: 0; top: calc(100% + 10px);
  background: var(--bg2); border: 1px solid var(--border2);
  border-radius: var(--rl); box-shadow: var(--shadow-menu);
  padding: 8px; z-index: 130;
  opacity: 0; visibility: hidden; transform: translateY(-6px) scale(.97);
  transform-origin: top var(--menu-origin, right);
  transition: opacity .16s ease, transform .16s ease, visibility .16s;
}
.pt-menu { width: 264px; }
/* Keyed on the two attributes chrome.js actually works in terms of, not on a
   list of component classes. It used to name .pt-acct and .pt-nav__item, and
   a menu whose class was not in that list simply never appeared: the script
   added .is-open and set aria-expanded="true" exactly as designed, and the
   panel stayed visibility:hidden. .pt-lang shipped that way and its menu has
   never opened — pressing it did nothing, with no error anywhere, because
   every moving part except this selector was working. A currency control added
   later was the second to inherit it, which is how it was found.

   So the rule now matches [data-chrome-menu] / [data-chrome-menu-panel], the
   same pair menus() and setOpen() use. A new menu that follows the script's
   contract is revealed by having followed it, rather than by someone also
   remembering to extend this list. */
[data-chrome-menu].is-open [data-chrome-menu-panel] {
  opacity: 1; visibility: visible; transform: translateY(0) scale(1);
}
.pt-menu__head {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 8px 12px; margin-block-end: 6px;
  border-block-end: 1px solid var(--border2);
}
.pt-acct__avatar--lg {
  width: 38px; height: 38px; font-size: 14px;
  background: var(--base-bg); color: var(--base-d);
}
.pt-menu__who {
  font-size: 13.5px; font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pt-menu__list { list-style: none; margin: 0; padding: 0; }
.pt-menu__link {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 10px; border-radius: var(--r);
  color: var(--text2); text-decoration: none; font-size: 13.5px; font-weight: 500;
  transition: background-color .15s ease, color .15s ease;
}
.pt-menu__link:hover { background: var(--bg3); color: var(--text); }
.pt-menu__link svg { opacity: .7; flex: none; }
.pt-menu__sep {
  margin-block-start: 6px; padding-block-start: 6px;
  border-block-start: 1px solid var(--border2);
}

/* ── The language switcher ───────────────────────────────────────────── */
/* Shares .pt-menu's card and the [data-chrome-menu] behaviour with the account
   menu — same shape, same script, no second implementation. */
.pt-lang { position: relative; }
.pt-lang__btn {
  display: flex; align-items: center; gap: 6px;
  background: transparent; border: 1px solid transparent; cursor: pointer;
  color: var(--on-dark-70); font: inherit; font-size: 13.5px; font-weight: 500;
  padding: 7px 10px; border-radius: 999px;
  transition: background-color .18s ease, border-color .18s ease, color .18s ease;
}
.pt-lang__btn:hover { background: var(--on-dark-10); border-color: var(--on-dark-14); color: var(--on-dark); }
.pt-lang__btn[aria-expanded="true"] .pt-acct__caret { transform: rotate(180deg); }
.pt-lang__name { white-space: nowrap; }
/* Eighteen locales is taller than a viewport on a phone, so the card scrolls
   rather than running off the screen with its last entries unreachable. */
/* A heading per section of the globe menu, which now carries language AND
   currency. Quiet enough that the choices stay the thing you read. */
.pt-menu__group {
  margin: 6px 0 2px; padding: 0 10px;
  font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text3);
}
.pt-menu__list + .pt-menu__group { margin-top: 10px; border-block-start: 1px solid var(--border2); padding-block-start: 10px; }
.pt-menu__sym { display: inline-block; min-width: 14px; font-weight: 600; }

.pt-menu--lang { width: 208px; max-height: min(60vh, 420px); overflow-y: auto; }
.pt-menu--lang .pt-menu__link.is-current { background: var(--bg3); color: var(--text); font-weight: 600; }

/* The breadcrumb trail, where a page gives one. It sits where the nav links
   would be, and the categories keep their place after it: a trail says where
   you are, they are how you go elsewhere. Wraps rather than truncating —
   a product name is long and the last step is the one worth reading. */
.pt-crumbs-wrap { min-width: 0; }
.pt-crumbs {
  display: flex; flex-wrap: wrap; align-items: center; gap: 2px 6px;
  list-style: none; margin: 0; padding: 0;
  font-size: 13.5px; min-width: 0;
}
.pt-crumbs__item { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
/* The separator is generated, not markup: a "/" typed between items is read
   aloud as part of the label by a screen reader. */
.pt-crumbs__item + .pt-crumbs__item::before {
  content: "/"; color: var(--text3); font-size: 12px;
}
.pt-crumbs__link { color: var(--text2); text-decoration: none; white-space: nowrap; }
.pt-crumbs__link:hover { color: var(--text); text-decoration: underline; }
/* The current page carries the weight, and is the one allowed to wrap. */
.pt-crumbs__here {
  color: var(--text); font-weight: 600;
  overflow-wrap: anywhere; min-width: 0;
}

/* ── The nav row ─────────────────────────────────────────────────────── */
.pt-nav {
  background: var(--bg2);
  border-block-end: 1px solid var(--border);
  box-shadow: var(--shadow-nav);
  position: relative; z-index: 110;
  transition: box-shadow .2s ease;
}
.pt-nav__inner { min-height: 54px; }
.pt-nav__links { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }

/* The underline grows from the middle out. It is on a ::before rather than on
   border-bottom so the resting state occupies the same box as the hover state
   and the row does not shift by a pixel when a link is pointed at. */
.pt-nav__link {
  position: relative;
  color: var(--text2); text-decoration: none;
  font-size: 14px; font-weight: 500;
  padding: 16px 10px;
  transition: color .18s ease;
}
.pt-nav__link::before {
  content: ""; position: absolute;
  inset-inline-start: 50%; inset-block-end: 0;
  width: 0; height: 2px; background: var(--base);
  transform: translateX(-50%);
  transition: width .25s ease;
}
.pt-nav__link:hover { color: var(--text); }
.pt-nav__link:hover::before { width: calc(100% - 20px); }
.pt-nav__link.is-current { color: var(--text); font-weight: 600; }
.pt-nav__link.is-current::before { width: calc(100% - 20px); }

.pt-nav__item { position: relative; display: flex; align-items: center; }
.pt-nav__item .pt-nav__link { padding-inline-end: 2px; }
.pt-nav__caret {
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 0; cursor: pointer;
  color: var(--text3); padding: 6px 8px 6px 4px;
  transition: transform .2s ease, color .18s ease;
}
.pt-nav__item:hover .pt-nav__caret { color: var(--text); }
.pt-nav__item.is-open .pt-nav__caret { transform: rotate(180deg); color: var(--text); }
/* The panel is left-aligned to its category, not right-aligned to the strip. */
.pt-nav__panel {
  inset-inline-end: auto; inset-inline-start: 0;
  --menu-origin: left;
  min-width: 210px; padding: 10px;
}
.pt-nav__panel a {
  display: block; padding: 8px 10px; border-radius: var(--r);
  color: var(--text2); text-decoration: none; font-size: 13.5px; font-weight: 500;
  white-space: nowrap;
  transition: background-color .15s ease, color .15s ease;
}
.pt-nav__panel a:hover { background: var(--bg3); color: var(--text); }

.pt-nav__cta {
  display: inline-flex; align-items: center; gap: 7px;
  background: var(--base); color: var(--on-base); text-decoration: none;
  padding: 9px 17px; border-radius: 999px; font-size: 13.5px; font-weight: 600;
  white-space: nowrap; flex: none;
  box-shadow: var(--shadow-card);
  transition: background-color .18s ease, box-shadow .18s ease, transform .18s ease;
}
.pt-nav__cta:hover {
  background: var(--base-d);
  box-shadow: var(--shadow-card-hover);
  transform: translateY(-1px);
}
.pt-nav__cta svg { opacity: .9; }

/* Rendered only where there is more than one thing to collapse — the partial
   leaves the button out otherwise, so the dashboard's single-link nav does not
   grow a hamburger that opens one link above a pill strip holding eight. Shown
   only under .chrome-js: a hamburger is a promise that something will open. */
.pt-nav__toggle {
  display: none; align-items: center; gap: 8px;
  background: transparent; border: 1px solid var(--border);
  border-radius: var(--r); cursor: pointer;
  color: var(--text2); font: inherit; font-size: 13.5px; font-weight: 500;
  padding: 8px 14px;
}

:where(.pt-acct__btn, .pt-nav__caret, .pt-nav__toggle):focus-visible,
:where(.pt-top__link, .pt-top__signin, .pt-nav__link, .pt-menu__link, .pt-nav__panel a):focus-visible {
  outline: 2px solid var(--base-l);
  outline-offset: 2px;
  border-radius: var(--r);
}

/* Both rows stick above the pills breakpoint; below it only the dark strip
   does (the next block), since the nav row there is a breadcrumb trail or a
   category list that would eat a phone's height for nothing. */
@media (min-width: 769px) {
  /* Both rows stick, as one block: the dark strip carries the cart and the
     account menu, which are what a reader scrolling a long page reaches for.
     .pt-chrome is a direct child of <body>, so it stays pinned for the whole
     page rather than only within a short parent. */
  .pt-chrome { position: sticky; top: 0; z-index: 120; }
  .pt-chrome.is-stuck { box-shadow: var(--shadow-nav-stuck); }
  /* WHAT ANYTHING ELSE ON THE PAGE HAS TO STICK BELOW. Raised from the 0px
     tokens.css declares, because this is the only breakpoint at which the nav
     is pinned and therefore the only one at which its height is an offset.
     The raise stays HERE, beside the rule it is conditional on: moved to
     tokens.css it would be a second copy of this breakpoint, free to drift
     from the one that decides whether the nav sticks at all.
     The nav is the only thing pinned to the viewport top, so every other
     sticky element on a portal page has to start where the nav ends or it
     scrolls underneath and loses its first rows — which is exactly what the
     product page's buy panel did with its own top: 20px.
     Published as a token rather than repeated as a number: the nav's height
     is NOT a constant. Its min-height is 54px, but it carries the breadcrumb
     trail, which wraps on a long product name, so a page's nav can be two
     rows where another page's is one, and the shop front has no nav row at
     all. chrome.js keeps --pt-chrome-h measured (both rows together); the
     124px here is strip + one-row nav, so a page with no script still clears
     the header instead of sliding under it. */
  :root { --pt-sticky-top: var(--pt-chrome-h, 124px); }
  /* Opening on hover as well as on click is what makes a menu bar feel like
     one. Guarded on a fine pointer: on a touchscreen "hover" fires on the tap
     that is also following the category's own link, which opens a panel the
     visitor never sees on a page they are already leaving. */
  @media (hover: hover) and (pointer: fine) {
    .pt-nav__item:hover .pt-nav__panel {
      opacity: 1; visibility: visible; transform: translateY(0) scale(1);
    }
  }
}

@media (max-width: 768px) {
  /* The row becomes a stacked list. Note what is NOT conditional on
     .chrome-js: the layout. Only the two collapses are, so the no-script
     rendering is this same list, fully expanded, rather than an empty
     band with a dead hamburger in it. */
  .pt-nav__inner { flex-wrap: wrap; padding-block: 10px; }
  .pt-nav__links {
    width: 100%; order: 3;
    flex-direction: column; align-items: stretch; gap: 0;
    padding-block-end: 10px;
  }
  .pt-nav__link { padding: 12px 4px; }
  .pt-nav__link::before { display: none; }
  .pt-nav__item { flex-wrap: wrap; justify-content: space-between; }
  .pt-nav__item .pt-nav__link { flex: 1; }
  .pt-nav__caret { padding: 12px 8px; }
  /* The panel stops floating and becomes an accordion: a 264px card anchored
     to a full-width row would hang off the side of the screen. */
  .pt-nav__panel {
    position: static; width: 100%; order: 3;
    box-shadow: none; border: 0; border-radius: 0;
    padding: 0 0 8px 14px; margin: 0;
    transform: none; transition: none;
    opacity: 1; visibility: visible;
  }

  /* And where there was nothing to collapse, there is nothing to keep. The
     dashboard's row is one link to the shop plus the membership CTA, and at
     this width both destinations are already on the page — the shop in the
     footer, membership in the pill strip, the drawer and the account menu —
     so the row is a lone button on an otherwise empty band. Taken away
     whole, for the same reason the toggle is: hiding what is inside a row
     leaves its 54px behind. The shop's row has categories, hence a toggle,
     and keeps its place. */
  .pt-nav--bare { display: none; }
  /* Off in the flow, not merely invisible: a hidden row that still occupies
     54px is how the nav ends up as a blank band with nothing in it. */
  .chrome-js .pt-nav__toggle { display: inline-flex; }
  /* Scoped to --collapsible: a nav with no toggle must keep its links,
     or its only destination is unreachable on a phone. */
  .chrome-js .pt-nav--collapsible .pt-nav__links { display: none; }
  .chrome-js .pt-nav--collapsible .pt-nav__links.is-open { display: flex; }
  .chrome-js .pt-nav__panel { display: none; }
  .chrome-js .pt-nav__item.is-open .pt-nav__panel { display: block; }
}

/* On a phone the dark strip stays in view — it carries the cart and the
   account menu. .pt-chrome sticks as a unit above 768px, and a sticky child
   only sticks within its parent, so here the wrapper steps out of the box
   tree and the strip sticks against <body> instead. Everything else that
   sticks on a phone (the product tabs) starts below it via
   --pt-sticky-top; chrome.js measures the strip as --pt-top-h, and 60px is
   its height at this width without script. */
@media (max-width: 768px) {
  .pt-chrome { display: contents; }
  .pt-top { position: sticky; top: 0; }
  .pt-chrome.is-stuck .pt-top { box-shadow: var(--shadow-nav-stuck); }
  :root { --pt-sticky-top: var(--pt-top-h, 60px); }
}

/* Below 640px the two rows stack their own contents rather than truncating:
   the brand keeps its name and the account button drops to its avatar. */
@media (max-width: 640px) {
  .pt-top__inner, .pt-nav__inner { padding: 0 11px; gap: 8px; }
  .pt-nav__inner { padding-block: 10px; }
  .pt-top__inner { min-height: 60px; }
  .pt-acct__name { display: none; }
  .pt-top__link { display: none; }
  /* The third control on this strip, and the one that tipped it over: with the
     switcher's label showing, .pt-top__right measured 217px inside a 390px
     viewport and "Sign in" was cut off mid-word on every page. The globe and
     caret alone still read as a language control, the same way the avatar
     alone reads as an account. This bar has no room left at 390px. */
  .pt-lang__name { display: none; }
  /* Squeezed to a two-line "Sign / in" once the bar tightened. */
  .pt-top__signin { white-space: nowrap; padding: 8px 13px; }
  .pt-menu { width: min(264px, calc(100vw - 28px)); }

  /* THE SIGNED-IN BAR: a visitor also gets .mobile-menu-btn on the left, on
     top of brand + switcher + cart + account. An earlier version of this
     rule freed room by dropping the wordmark to a bare icon below 640px —
     it fit, but a lone mark with no name reads as "no logo" rather than as
     a simplified one. The wordmark stays; the room for it comes from
     trimming every other control on the bar by a few px instead (padding,
     gaps, the avatar and hamburger), none of which needed its full desktop
     size on a 60px-tall mobile strip anyway. */
  .mobile-menu-btn { width: 32px; height: 32px; }
  .pt-lang__btn { gap: 4px; padding: 6px 7px; }
  .pt-cart { padding: 4px; }
  .pt-acct__btn { gap: 7px; padding: 4px 6px 4px 4px; }
  .pt-acct__avatar { width: 27px; height: 27px; }
  .pt-top__right { gap: 4px; }
  /* The fused logo (icon + "startmy marketplace" spelled out on one line) is
     wide at its desktop size, so it scales with the viewport here: 22px at
     360px (a common Android width) up to 26px, which is what the shop bar's
     switcher/cart/account cluster leaves room for at each width.
     flex-shrink:0, on both the image and the link around it, is
     load-bearing: without it flexbox shrinks a replaced element by
     squashing its box rather than by scaling it, so a tight viewport
     would distort the logo (and run it into the switcher) instead of
     legibly holding its size. A viewport narrower than 360px scrolls this
     one strip horizontally, which is a visible seam but not a broken one —
     the alternative is the same distortion .pt-top__brand's min-width:0
     exists to avoid for a plain-text brand (see .brand-word below, still
     the shape a white-labelled deployment's wordmark-only brand needs). */
  .pt-top__brand img.pt-top__logo { height: clamp(20px, 6.2vw, 26px); flex-shrink: 0; }
  /* The dashboard's bar also carries the menu button, so its logo keeps the
     20px that fits beside it at 360px. */
  .pt-top__inner:has(.mobile-menu-btn) .pt-top__logo { height: 20px; }
  .pt-top__brand img.pt-top__logo-compact { display: none; }
  .pt-top__brand:has(img.pt-top__logo) { min-width: auto; flex-shrink: 0; }
  .pt-top__brand { min-width: 0; }
  .pt-top__brand .brand-word {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
  }
}

/* An animated underline and a menu that scales into place are decoration. The
   menus still open and close — only the movement goes. */
@media (prefers-reduced-motion: reduce) {
  .pt-nav__link::before,
  .pt-menu, .pt-nav__panel,
  .pt-acct__caret, .pt-nav__caret, .pt-nav__cta,
  .pt-foot__cols nav a, .pt-foot__col nav a {
    transition: none;
  }
  .pt-nav__cta:hover { transform: none; }
  .pt-foot__cols nav a:hover, .pt-foot__col nav a:hover { transform: none; }
}

.pt-foot { background: var(--bg2); border-block-start: 1px solid var(--border); margin-block-start: 64px; }
/* One line of colour across the seam between page and footer — the only
   accent on an otherwise flat, bordered footer, and enough on its own to read
   as a deliberate edge rather than the same hairline every card on the page
   already draws. --base-l is the accent that differs between the dashboard's
   and the shop's skins (tokens.css re-declares it under body.shop); --brand-mark
   is the one that deliberately never does, per its own comment there — so the
   line always carries one colour that matches the surface it sits on and one
   that stays the brand's regardless of which skin is loaded. */
.pt-foot::before {
  content: '';
  display: block;
  height: 3px;
  background: linear-gradient(90deg, var(--base-l) 0%, var(--brand-mark) 100%);
}
/* Same 78rem as .pt-top__inner/.pt-nav__inner above and the shop's own
   .shop-main, so the header, page content and footer share one left/right
   edge instead of each column picking its own. */
.pt-foot__inner { max-width: 78rem; margin: 0 auto; padding: 64px 20px 28px; }
/* The brand column runs wider than the three link columns beside it — a
   tagline wrapped to match a nav column's width read as cramped where the
   links had room to spare. */
.pt-foot__cols {
  display: grid;
  grid-template-columns: minmax(220px, 1.7fr) repeat(auto-fit, minmax(140px, 1fr));
  gap: 40px;
}
/* .pt-top__brand is styled for the dark strip; on the footer's pale
   background it needs its own colour rather than on-dark's white-on-white. */
.pt-foot .pt-top__brand { color: var(--text); font-size: 17px; }
.pt-foot .pt-top__brand img.pt-top__logo { height: 30px; }
.pt-foot__cols p { color: var(--text2); font-size: 13.5px; line-height: 1.6; margin: 12px 0 0; max-width: 40ch; }
/* The reassurance pill under the brand tagline. Same convention checkout's
   own .co-trust-row__item already set — icon in --teal, chip fill in
   --teal-bg/--teal-border — so "secure" reads as the one colour it means
   everywhere it appears rather than the footer inventing a second one. */
.pt-foot__trust {
  display: inline-flex; align-items: center; gap: 6px;
  margin-block-start: 16px; padding: 6px 12px;
  background: var(--teal-bg); border: 1px solid var(--teal-border);
  border-radius: 999px;
  color: var(--teal-d); font-size: 12px; font-weight: 600;
}
.pt-foot__trust svg { flex: none; color: var(--teal); }
/* h3 only exists in the shop's richer footer (_footer.php); the dashboard's
   single nav (portal_footer.php) has no heading and is untouched by it. */
.pt-foot__col h3 {
  font-family: var(--display); font-weight: 600; font-size: 12.5px;
  letter-spacing: .03em; text-transform: uppercase;
  color: var(--text3); margin: 0 0 16px;
}
.pt-foot__cols nav, .pt-foot__col nav { display: flex; flex-direction: column; gap: 11px; }
.pt-foot__cols nav a, .pt-foot__col nav a {
  color: var(--text2); text-decoration: none; font-size: 13.5px;
  width: fit-content; transition: color .18s ease, transform .18s ease;
}
.pt-foot__cols nav a:hover, .pt-foot__col nav a:hover { color: var(--base); transform: translateX(2px); }
/* The shop footer's Company and Legal columns, wrapped together so the phone
   breakpoint below can give them one shared row instead of each stacking to
   the full width the single-column layout freed up — see _footer.php's own
   comment on the wrapper. display:contents keeps the wrapper out of the grid
   entirely above that breakpoint, so desktop's layout is exactly what it was
   with no wrapper at all. */
.pt-foot__col-pair { display: contents; }
.pt-foot__bottom {
  margin-block-start: 44px; padding-block-start: 20px;
  border-block-start: 1px solid var(--border);
  color: var(--text3); font-size: 12.5px;
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px;
}
/* The bottom bar's own small nav — Terms/Privacy on the dashboard's simpler
   footer, which (unlike the shop's) has no dedicated Legal column to carry
   them. A middle dot between entries rather than a border: at 12.5px a
   1px divider reads as a stray line, the dot reads as punctuation. */
.pt-foot__legal { display: flex; align-items: center; gap: 14px; list-style: none; margin: 0; padding: 0; }
.pt-foot__legal a { color: var(--text3); text-decoration: none; transition: color .18s ease; }
.pt-foot__legal a:hover { color: var(--base); }
.pt-foot__legal a + a::before { content: '·'; margin-inline-end: 14px; color: var(--text3); }
@media (max-width: 640px) {
  .pt-foot__inner { padding: 44px 20px 24px; }
  .pt-foot__cols { grid-template-columns: 1fr; gap: 32px; }
  /* Company and Legal side by side, one row, rather than each stacking to
     the full column width a single-column grid gives everything else here —
     two short lists of links read as a pair, not as two unrelated sections. */
  .pt-foot__col-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 32px 20px; }
  .pt-foot__bottom { justify-content: center; text-align: center; }
}

/* The wordmark is one inline box, so a flex parent's gap spaces it from the
   logo mark and never falls between its own words. See portal_wordmark(). */
.brand-word { display: inline; white-space: nowrap; }

/* The wordmark is one inline box, so a flex parent's gap spaces it from the
   logo mark and never falls between its own words. See portal_wordmark(). */
.brand-word { display: inline; white-space: nowrap; }

/* The wordmark's accent. Lives here because both the shop and the dashboard
   render the same wordmark and only this sheet is loaded by both. On .pt-top's
   dark strip --amber is the light-ground value and reads at 3.37:1, so that
   surface takes --amber-l. */
.brand-tld { color: var(--amber); }
.pt-top .brand-tld { color: var(--brand-mark); }
