/* ===================================================================
   hostna OS — WordPress glue.

   Loaded on BOTH surfaces: the application at / and every document page.

   It exists because assets/os/css/os.css is the design system and is kept in
   step with prototype/hostna-os — it knows nothing about WordPress. Anything
   WordPress or a plugin injects into the page is therefore unstyled, and since
   hostna_os_drop_legacy_layer() dequeues hostna-v6.css (the sheet that used to
   dress those pieces) on both surfaces, "unstyled" means raw browser defaults
   on a glass desktop. This file dresses exactly those pieces and nothing else,
   so os.css stays a clean copy of the prototype's.
   =================================================================== */

/* ── the appearance signal for native controls ──────────────────────
   The template pins the palette with data-theme, but the CSS color-scheme
   property was left at `normal`, so the browser kept rendering native widgets
   to the OPERATING SYSTEM's preference. On a machine set to dark that painted
   unticked checkboxes as solid black squares — indistinguishable from a ticked
   one — including the two DSA Art. 16 declarations on /report-abuse/ and the
   § 356 BGB consent on /kuendigung/, where a reporter genuinely cannot tell
   which statements they have confirmed. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

/* ── cookie consent bar ─────────────────────────────────────────────
   inc/consent.php prints this on wp_footer on every page, and hostna-v6.css
   was the only sheet that ever styled .consent-bar, .consent-bar__actions,
   .button--primary and .button--ghost. Without it the bar rendered as an
   ordinary paragraph at the very bottom of the document — not fixed, no
   background, no padding — with its two buttons stripped to bare text by
   os.css's own `button{background:none;border:0}` reset. A consent control
   that does not look like a control is not a consent control. */
.consent-bar {
  position: fixed;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  z-index: 90;
  width: min(880px, calc(100% - 24px));
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px 20px;
  padding: 16px 20px;
  border-radius: 20px;
  background: linear-gradient(160deg, var(--glass-a), var(--glass-b));
  -webkit-backdrop-filter: blur(var(--blur)) saturate(180%);
  backdrop-filter: blur(var(--blur)) saturate(180%);
  border: 1px solid var(--stroke);
  box-shadow: var(--shadow-win);
  color: var(--ink-2);
  font-size: 13px;
  line-height: 1.55;
}

.consent-bar p { margin: 0; flex: 1 1 320px; min-width: 0; }
.consent-bar a { color: var(--accent); text-underline-offset: 3px; }

.consent-bar__actions {
  display: flex;
  gap: 10px;
  flex: 0 0 auto;
  margin-left: auto;
}

.consent-bar .button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 40px;
  padding: 0 18px;
  border: 1px solid transparent;
  border-radius: 12px;
  font: inherit;
  font-size: 13.5px;
  font-weight: 560;
  cursor: pointer;
  white-space: nowrap;
  transition: background .18s, transform .18s var(--ease), filter .18s;
}
.consent-bar .button:hover { transform: translateY(-1px); }

.consent-bar .button--primary {
  background: linear-gradient(120deg, var(--btn), var(--btn-2));
  color: #fff;
  box-shadow: var(--shadow-pop);
}
.consent-bar .button--primary:hover { filter: brightness(1.06); }

/* "Essential only" must not read as the lesser option: under the GDPR refusing
   has to be as easy as accepting, so it gets the same size and weight and only
   a quieter fill. */
.consent-bar .button--ghost {
  background: var(--tile-2);
  border-color: var(--stroke);
  color: var(--ink);
}
.consent-bar .button--ghost:hover { background: var(--tile-hover, var(--tile-2)); }

/* The application's dock sits at z-index 25 and the page dock at 40; both are
   along the same bottom edge, so the bar lifts them out of its way while it is
   on screen. os.js already toggles nothing here — the bar removes itself once a
   choice is stored, and these rules go with it. */
/* Scoped with .hostna-os to reach (0,3,1). At the plain `body:has(...)` form
   this ties `body.hostna-os-page .os-pagedock` in os-page.css at (0,2,1) — and
   os-page.css is enqueued after this file, so the tie went to the dock's own
   `bottom:12px` and the two bars sat on top of each other. */
body.hostna-os:has(.consent-bar) .rail,
body.hostna-os:has(.consent-bar) .os-pagedock { bottom: calc(16px + 96px); }
/* ⚠ THE APPLICATION'S WINDOW MUST END ABOVE THE RAISED DOCK. The document
   pages got this in 8.88.0 (os-desktop.css pads .scroll); the app shell never
   did, so while the notice was open the dock and the bar sat over the last
   ~130px of every screen — and a sticky "Buy this machine" / "Deploy now"
   pinned to the scrollport's bottom was pinned under them (measured 0/11
   scroll positions at 390x844, 2026-09-10). The stage's bottom padding grows by
   exactly the dock's lift, so the scrollport — and everything sticky in it —
   stops where the dock begins. */
body.hostna-os-app:has(.consent-bar) .stage { padding-bottom: calc(var(--os-edge) + 96px); }

@media (max-width: 700px) {
  .consent-bar {
    bottom: 8px;
    width: calc(100% - 16px);
    padding: 14px 16px;
    border-radius: 18px;
    font-size: 12.5px;
  }
  .consent-bar__actions { margin-left: 0; width: 100%; }
  .consent-bar .button { flex: 1 1 0; }
  body.hostna-os:has(.consent-bar) .rail,
  body.hostna-os:has(.consent-bar) .os-pagedock { bottom: calc(var(--os-dock-b) + 132px); }
  /* the phone lift is 132px (above) — the app's stage follows it */
  body.hostna-os-app:has(.consent-bar) .stage { padding-bottom: calc(var(--os-edge) + 132px); }
}

/* ── the window-bar mark ────────────────────────────────────────────
   Present in the markup on both surfaces but shown ONLY on the phone layout,
   where it replaces the menubar's identity. Above 700px the menubar carries
   the mark and a second one in the window bar is just the brand twice. */
.os-winbrand { display: none; }

/* ═══════════════════════════════════════════════════════════════════
   WINDOW CONTROLS — every width, not just the phone.

   The three lights started as ornament on the left, in the macOS position,
   doing nothing but telling a joke in a toast. They are the window's controls
   now, on both surfaces and at every size: home, fullscreen, back. So they
   move to the right edge, where a window's controls live on Windows and where
   the thumb already reaches on a phone, and the blue action button that used
   to occupy that corner gives up the seat — the dots replace it rather than
   crowd it.

   The order is reversed for the same reason it is on the phone: the DOM keeps
   red-yellow-green (correct if these were ever macOS buttons again), and
   `row-reverse` paints them green, yellow, red, so the destructive-looking one
   sits furthest out.
   ═══════════════════════════════════════════════════════════════════ */

/* The action button the dots replace. */
body.hostna-os .wt-act { display: none; }

body.hostna-os .lights {
  order: 9;
  margin-left: auto;
  flex-direction: row-reverse;
  flex: 0 0 auto;
}

/* They are controls, so they need a target rather than an 11px dot. The dot
   stays 11px on a desktop; the hit area around it does not have to be visible
   to be clickable. */
body.hostna-os .lights b {
  position: relative;
  cursor: pointer;
  transition: transform .16s var(--ease), filter .16s;
}
body.hostna-os .lights b::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 28px;
  height: 28px;
  transform: translate(-50%, -50%);
}
body.hostna-os .lights b:hover { filter: brightness(1.08); transform: scale(1.12); }
body.hostna-os .lights b:active { transform: scale(.88); }
body.hostna-os .lights b:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 50%;
}

/* Yellow is the only one that can be unavailable — no Fullscreen API on an
   iPhone. os-chrome.js hides it rather than leaving a dot that does nothing. */
body.hostna-os .lights b.is-unavailable { display: none; }
body.hostna-os .lights b.is-on { filter: brightness(1.15) saturate(1.3); }

/* ── the left edge closes up behind them ────────────────────────────
   With the lights gone from the left there is a hole where they used to sit,
   and os.css centres the title and the tab pill on the assumption that
   something occupies both ends. Nothing does now, so the title, the
   breadcrumb and the tab switcher all sit flush left and the bar reads from
   one side instead of floating in the middle of a gap. */
body.hostna-os .win.no-tabs .wt {
  position: static;
  left: auto;
  transform: none;
  margin: 0;
}
body.hostna-os .wt-tabs { margin: 0 auto 0 0; }

/* ═══════════════════════════════════════════════════════════════════
   NO SCROLLBARS. Every surface, every width, every scroller.

   A window in this design has no scrollbar for the same reason it has no menu
   bar on a phone: the chrome is the point, and a light grey rail pinned inside
   the right edge of the glass is chrome nobody asked for. It is also the one
   piece of the window that is not ours — its width, its colour and its resting
   opacity come from the engine, so it looks different on every browser and
   there is no version of it that agrees with the rest of the frame.

   WHY A CATCH-ALL AND NOT A LIST. Eight scroll containers declare
   `scrollbar-width: thin` independently — .scroll and .flow-scroll in the
   application, .metal-list, .dom-results, .tool-out, .rig-mid, and .cc-scroll
   / .ad-scroll in the two injected panel sheets — and each carries its own
   ::-webkit-scrollbar width, thumb and track. That is the same "one number,
   many owners" shape this month has been spent removing, and naming them here
   would recreate it: the ninth scroller somebody adds next week would arrive
   with a scrollbar again. `body.hostna-os *` reaches every one of them at
   (0,1,1), which beats the plain-class rules they are written at.

   NOTHING STOPS SCROLLING. This hides the indicator, not the overflow: wheel,
   touch, trackpad, Page Up/Down, Home/End, arrow keys and the caret all behave
   exactly as before, and every scroller keeps `overflow:auto` and its own
   `overscroll-behavior`. What is genuinely given up is the pointer affordance
   — you can no longer grab the bar and drag it — and the position indicator
   that came free with it. That is the owner's call, made knowing both.
   ═══════════════════════════════════════════════════════════════════ */
body.hostna-os,
body.hostna-os * {
  scrollbar-width: none;
  -ms-overflow-style: none;
}
body.hostna-os::-webkit-scrollbar,
body.hostna-os *::-webkit-scrollbar {
  width: 0;
  height: 0;
  display: none;
}

/* ═══════════════════════════════════════════════════════════════════
   MOBILE CHROME — one bar instead of two.

   On a phone the desktop metaphor was costing two full-width strips before a
   single word of content: the OS menubar (mark, search, cart, appearance,
   clock) and then the window's own title bar. Together they ate ~90px of a
   ~640px screen and repeated the brand twice.

   Below 700px the menubar goes entirely and its identity moves down into the
   window bar, which becomes the only piece of chrome at the top. The traffic
   lights move to the right edge and stop being decoration: they become the
   three controls a phone actually needs.

   REVERSED ON PURPOSE. The DOM order is red, yellow, green — correct for the
   macOS convention this window borrows on a desktop. `row-reverse` paints them
   green, yellow, red from left to right, so the red one lands in the far
   corner where a thumb reaches it, and the order still reads outward from the
   least destructive to the most.

   Everything here is scoped to both surfaces (.hostna-os covers the
   application and the document pages), because the point is that a phone
   visitor cannot tell which of the two they are on.
   ═══════════════════════════════════════════════════════════════════ */
@media (max-width: 700px) {

  /* the whole top strip goes; the window bar is the only chrome left */
  body.hostna-os .menubar { display: none; }

  /* ── ONE WINDOW BOX, FOR EVERY SURFACE ────────────────────────────
     Four surfaces draw the same window — the application, the document pages,
     and the two panels inside the application — and until now each carried its
     own numbers. Measured at 390px before this block existed: the application
     inset its window 10px from the screen edge, the document pages 8px; the
     application reserved `--nav-h + 2` under it, the pages `--nav-h + 20`. Two
     surfaces a visitor is not supposed to be able to tell apart, four pixels
     and eighteen pixels out of step, kept in step by hand.

     So the box is four numbers, declared once on body.hostna-os — which both
     surfaces carry — and every rule below reads them instead of restating a
     literal. A surface that drifts now is a surface that stopped inheriting,
     which is a bug with a cause rather than a number someone forgot.

     WHERE THEY COME FROM, since none of them is a preference:

     --os-gap 6px   The window has to stay a window: a full-bleed sheet with no
                    margin is a page, and the whole design rests on the visitor
                    seeing a desktop behind glass. 6px is the smallest margin
                    that still reads as one at 390px against the 20px corner
                    radius below — the curve is what makes it legible, so the
                    two numbers move together. It buys 8px of window width over
                    the 10px it replaces.

     --os-bar 44px  The title bar's tallest occupant is a traffic light's hit
                    area, and that is 34px (WCAG 2.5.8 wants 24; this bar has
                    had 34 since the mobile chrome was written). 34 + 5 + 5 = 44
                    keeps the target intact and returns 8px of the 52 the bar
                    was taking. Below 44 the dots start touching the edges.

     --os-pad 12px  The inner inset, .scroll's own padding. 16px was inherited
                    from a `max-height: 900px` rule written for laptops, not for
                    phones. 12px still separates a card's shadow from the
                    window's inner edge and hands 8px back to the cards.

     Net at 390x780: the window is 8px wider, 3px higher, its bar is 8px
     shorter and its content 16px wider than it was — in a place where that
     is the difference between two cards and three. */
  /* THE DOCK'S OWN NUMBERS ON A PHONE, and they are on :root rather than on
     body.hostna-os for a reason that is easy to undo by accident: --os-edge
     is ONE derived declaration in os.css, sitting on :root, and it resolves
     against whichever of these three won the cascade. Move them down to the
     body and --os-edge stays the desktop's — inherited, already resolved —
     and the window's floor quietly stops following the dock.

     52 is 38px of button + 2x6 padding + 2x1 border. Three rules used to
     carry these as literals: the launcher sheet cleared `8px + 52px + 10px`,
     the consent bar `8px + 132px`, and the dock declared the 8px twice, once
     per surface.

     THE SYSTEM OWNS THE LAST FEW MILLIMETRES. Both templates ship
     `viewport-fit=cover` (front-page.php, template-parts/os-document.php), so
     at a flat 8px this bar would sit inside the iOS home indicator and the
     Android gesture strip — where the OS takes the touch before the page ever
     sees it. It did not matter while the dock was 72px up resting on the
     window; it is the whole thing now that the dock is down in the strip. The
     inset is added to the offset, so the bar rides above the system's own
     furniture and --os-edge grows by the same amount. */
  :root {
    --os-dock-b:   calc(8px + env(safe-area-inset-bottom, 0px));
    --os-dock-h:   52px;
    --os-dock-gap: 8px;
  }

  body.hostna-os {
    --os-gap:  6px;
    --os-bar:  44px;
    --os-pad:  12px;

    /* The inner inset's vertical half. It exists because the horizontal half
       alone left a split the unification was supposed to close, just on the
       other axis: .scroll's vertical padding is chosen by `@media
       (max-height: 900px)` (os.css:1393), so a 390x844 phone renders 12px and
       a 412x915 one renders os.css:287's 14px. Same window, same surface, two
       phones, two answers. 12px is not a new number — it is the short-viewport
       value, re-keyed from height to width so a phone matches a phone. */
    --os-padv: 12px;

    /* the corner is part of the margin's job — see --os-gap */
    --r-win:   20px;
  }

  /* The stage starts at the top of the screen now that nothing is above it,
     and it is the same box on both surfaces. os-page.css declares the identical
     padding at the identical specificity in a later file: that is deliberate
     redundancy, not a mistake. Whichever of the two wins the tie, the value it
     wins with is the same one. */
  body.hostna-os .stage {
    top: 0;
    padding: var(--os-gap) var(--os-gap) var(--os-edge);
  }

  /* The window's inner inset. Horizontal only: how far a card sits from the
     window's edge is the thing being unified, and the vertical rhythm above and
     below a document is a different question that each surface may answer for
     itself. */
  body.hostna-os .scroll:not(.flat):not(.ad-host),
  body.hostna-os .flow-scroll { padding-inline: var(--os-pad); }

  /* .flow-scroll is a SIBLING of .scroll, not a variant of it — the plan,
     order, cart and studio views all build `<div class="flow-scroll">`
     (assets/os/js/views/*.js), and it carries its own padding at os.css:1143
     and os.css:1311. Written as `.scroll` alone this rule reached the home
     screen and the panels and missed the entire purchase flow, which then sat
     14px from the window edge while everything else sat at 12.

     .ad-host is excluded rather than left to source order. panel-admin.js
     declares `.scroll.ad-host{padding:0}` at (0,2,0) with a comment saying it
     "beats plain .scroll whatever the sheet order" — true of `.scroll`, false
     of this rule at (0,3,1), which had quietly given the admin panel a 12px
     inset its own layout is built to do without. Excluding it here keeps that
     contract true instead of relying on which sheet loads last. */

  /* The vertical half, application only. The document surface answers this
     question deliberately and differently (os-page.css:127, 20px over 30px for
     a page of running text) and os-page.css:660 forbids height queries
     outright, so there is nothing to unify there — the split being closed is
     the one inside the application. */
  body.hostna-os-app .scroll:not(.flat):not(.ad-host),
  body.hostna-os-app .flow-scroll { padding-block: var(--os-padv); }

  body.hostna-os .winbar {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 12px;
    /* both, and in this order: os.css sets `height: 52px` at (0,1,0), so
       min-height alone left the bar exactly as tall as it was. */
    height: var(--os-bar);
    min-height: var(--os-bar);
  }

  /* the mark, where the lights used to be */
  body.hostna-os .os-winbrand {
    order: 0;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    flex: 0 0 auto;
    color: var(--ink);
    text-decoration: none;
  }
  body.hostna-os .os-winbrand svg { width: 20px; height: 20px; }
  body.hostna-os .os-winbrand strong {
    font-size: 12.5px;
    font-weight: 700;
    letter-spacing: .14em;
  }

  /* the app's own back chevron is redundant now that green is the back button */
  body.hostna-os .winbar .navbar { display: none; }
  body.hostna-os .winbar .wt { order: 1; }

  /* a little more room between them where the thumb is the pointer */
  body.hostna-os .lights { gap: 10px; }

  /* They are controls here, not ornament: 14px dots inside a 34px hit area, so
     the target clears WCAG 2.5.8 without three fat circles crowding the bar. */
  body.hostna-os .lights b {
    position: relative;
    width: 14px;
    height: 14px;
    cursor: pointer;
    transition: transform .16s var(--ease), filter .16s;
  }
  body.hostna-os .lights b::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 34px;
    height: 34px;
    transform: translate(-50%, -50%);
  }
  body.hostna-os .lights b:active { transform: scale(.88); }
  body.hostna-os .lights b:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    border-radius: 50%;
  }

  /* Yellow is the only one that can be unavailable: iPhone Safari has no
     Fullscreen API. os-chrome.js sets this rather than leaving a dead dot. */
  body.hostna-os .lights b.is-unavailable { display: none; }

  /* While fullscreen is on, the yellow dot reads as engaged. */
  body.hostna-os .lights b.is-on { filter: brightness(1.15) saturate(1.3); }
}

/* ═══════════════════════════════════════════════════════════════════
   THE LAUNCHER — mobile only.

   Removing the menubar bought the phone ~40px of height and cost it three
   controls that had nowhere else to go: search, the cart, and the appearance
   toggle. The dock could not absorb them — nine icons already overflow it and
   scroll sideways.

   So the dock's last utility slot becomes a key, in the sense a Windows key is
   one: press it and the things that did not fit come up. The mark is the cloud
   rather than a four-pane grid, because the dock already has a four-square
   "Apps" icon and because a brand key reads as "this site's menu".

   Deliberately NOT full screen. It rises to at most 62dvh and leaves the dock,
   the window bar and a slice of the page visible behind it, so it reads as a
   panel over the desktop rather than a new page you have to escape.
   ═══════════════════════════════════════════════════════════════════ */
@media (max-width: 700px) {

  /* Network and Settings move off the dock and into the launcher; the first
     of the two slots is where the key goes. Hidden, not removed — on the
     application the handler that opens each screen is bound to these very
     buttons, and the launcher clicks them.

     Two different shapes to hide. The application's dock holds the buttons
     directly, so hiding the button is enough. The document pages wrap each
     anchor in an <li>, and an emptied <li> still consumes the list's 6px gap —
     so there the ROW goes, matched on the href the template already writes
     rather than on a class JavaScript would have to add first. */
  body.hostna-os .rail-btn[data-go="network"],
  body.hostna-os .rail-btn[data-go="settings"] { display: none; }

  body.hostna-os .os-pagedock li:has(> a[href$="#network"]),
  body.hostna-os .os-pagedock li:has(> a[href$="#settings"]) { display: none; }

  body.hostna-os .os-start {
    position: relative;
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 38px;
    height: 38px;
    padding: 0;
    border: 0;
    border-radius: 13px;
    background: color-mix(in srgb, var(--btn) 16%, transparent);
    color: var(--ink);
    cursor: pointer;
    transition: background .2s, transform .2s var(--ease);
  }
  body.hostna-os .os-start svg { width: 21px; height: 21px; }
  body.hostna-os .os-start:active { transform: scale(.9); }
  body.hostna-os .os-start[aria-expanded="true"] {
    background: color-mix(in srgb, var(--btn) 34%, transparent);
  }
  body.hostna-os .os-start:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }

  /* ── the sheet ────────────────────────────────────────────────── */
  body.hostna-os .os-launch {
    position: fixed;
    left: 50%;
    transform: translateX(-50%) translateY(14px);
    /* the dock's own numbers, not a copy of them — see --os-dock-b/h.
       The literals were correct only below 561px: between 561 and 700 the
       app's dock was 60-62px tall, so the sheet overlapped it by 8-10px. */
    bottom: calc(var(--os-dock-b) + var(--os-dock-h) + 10px);
    z-index: 80;
    width: calc(100% - 16px);
    max-width: 460px;
    max-height: 62dvh;
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 14px;
    border-radius: 22px;
    background: linear-gradient(160deg, var(--glass-a), var(--glass-b));
    -webkit-backdrop-filter: blur(var(--blur)) saturate(180%);
    backdrop-filter: blur(var(--blur)) saturate(180%);
    border: 1px solid var(--stroke);
    box-shadow: var(--shadow-win);
    overflow-y: auto;
    overscroll-behavior: contain;
    opacity: 0;
    visibility: hidden;
    transition: opacity .2s var(--ease), transform .26s var(--ease), visibility .26s;
  }
  body.hostna-os .os-launch.is-open {
    opacity: 1;
    visibility: visible;
    transform: translateX(-50%) translateY(0);
  }

  /* A scrim, so a tap anywhere else closes it and the page behind stops
     competing for attention. Light enough that the desktop still shows. */
  body.hostna-os .os-launch-scrim {
    position: fixed;
    inset: 0;
    z-index: 79;
    background: rgba(4, 6, 12, .28);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
    opacity: 0;
    visibility: hidden;
    transition: opacity .2s, visibility .2s;
  }
  body.hostna-os .os-launch-scrim.is-open { opacity: 1; visibility: visible; }

  /* ── search ───────────────────────────────────────────────────── */
  body.hostna-os .os-launch__search {
    display: flex;
    align-items: center;
    gap: 9px;
    height: 44px;
    padding: 0 13px;
    border-radius: 14px;
    background: var(--sunk);
    border: 1px solid var(--stroke-2);
  }
  body.hostna-os .os-launch__search svg {
    width: 17px; height: 17px; flex: 0 0 auto;
    fill: none; stroke: var(--ink-3); stroke-width: 1.8; stroke-linecap: round;
  }
  body.hostna-os .os-launch__search input {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    background: none;
    color: var(--ink);
    font: inherit;
    font-size: 15px;   /* 16px or more avoids iOS zooming the page on focus */
  }
  body.hostna-os .os-launch__search input:focus { outline: none; }

  /* ── rows ─────────────────────────────────────────────────────── */
  body.hostna-os .os-launch__row {
    display: flex;
    align-items: center;
    gap: 11px;
    width: 100%;
    min-height: 52px;
    padding: 10px 13px;
    border: 1px solid var(--stroke-2);
    border-radius: 15px;
    background: var(--tile);
    color: var(--ink);
    font: inherit;
    font-size: 14px;
    font-weight: 560;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
    transition: background .18s, transform .18s var(--ease);
  }
  body.hostna-os .os-launch__row:active { transform: scale(.985); }
  body.hostna-os .os-launch__row svg {
    width: 19px; height: 19px; flex: 0 0 auto;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
  }
  body.hostna-os .os-launch__row em {
    font-style: normal;
    font-size: 12px;
    font-weight: 400;
    color: var(--ink-3);
    margin-left: auto;
    text-align: right;
  }
  body.hostna-os .os-launch__row.is-primary {
    background: linear-gradient(120deg, var(--btn), var(--btn-2));
    border-color: transparent;
    color: #fff;
    box-shadow: var(--shadow-pop);
  }
  body.hostna-os .os-launch__row.is-primary em { color: rgba(255, 255, 255, .82); }

  /* the little count bubble on the cart row */
  body.hostna-os .os-launch__count {
    display: inline-grid;
    place-items: center;
    min-width: 22px;
    height: 22px;
    padding: 0 6px;
    border-radius: 99px;
    background: var(--go);
    color: #06210c;
    font-size: 11.5px;
    font-weight: 700;
  }

  body.hostna-os .os-launch__grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
  }
  body.hostna-os .os-launch__tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 7px;
    min-height: 74px;
    padding: 10px 6px;
    border: 1px solid var(--stroke-2);
    border-radius: 15px;
    background: var(--tile);
    color: var(--ink-2);
    font: inherit;
    font-size: 11.5px;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    transition: background .18s, transform .18s var(--ease);
  }
  body.hostna-os .os-launch__tile svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
  body.hostna-os .os-launch__tile:active { transform: scale(.96); }

  body.hostna-os .os-launch__label {
    font-size: 10.5px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--ink-3);
    margin: 2px 0 -4px 2px;
  }
}

/* Nothing of the launcher exists above the phone layout. */
@media (min-width: 701px) {
  .os-start, .os-launch, .os-launch-scrim { display: none !important; }
}

/* ── the page dock is fixed furniture ─────────────────────────
   This file used to carry the pages' half of the dock auto-hide: a transition
   on `.os-pagedock`, the `html.dock-auto` / `.dock-shown` pair that slid it off
   the bottom edge and back, and the `--os-free` padding the .stage claimed
   while it was away. Removed with the rest of the mechanism — see
   "THE DOCK STAYS PUT" in os.css for what it was and why it went.

   `.os-pagedock` keeps the offsets it already had (os-page.css: 12px, and
   --os-dock-b below 700px), so the bar a visitor sees on a document is the
   same bar in the same place on every load. */

/* ── the Control Center belongs to the sheet on a phone ─────────────
   Above 700px the dock IS the menu, so the Control Center sits in it. Below,
   the launcher exists and already carries "Open Control Center" as its own
   row — and the dock does not have the width to spare. Measured at 390px with
   the icon in place: the dock's content ran to 446px inside a 376px box (368
   when that was measured, before the dock's side margin was tied to --os-gap),
   so
   the Control Center was the one slot pushed past the Start key and off the
   right-hand edge, reachable only by scrolling a bar most people will not
   think to scroll. One entrance per screen, and on a phone the entrance is
   the sheet. */
@media (max-width: 700px) {
  body.hostna-os .rail-btn[data-go="client"] { display: none; }
  body.hostna-os-page .os-pagedock li[data-os-go="client"] { display: none; }
}
