body {
    font-family: 'Quicksand', sans-serif;
    font-weight: 500;
}

.pb-scroll::-webkit-scrollbar {
    width: 3px;
    height: 3px;
}
.pb-scroll::-webkit-scrollbar-thumb {
    background-color: #06b07c; /* Emerald-500 */
    border-radius: 6px;
}
.pb-scroll::-webkit-scrollbar-track {
    background-color: #0c6f48; /* Emerald-100 */
}

/* ── Landing page motion ────────────────────────────────────────────────────
   Everything here is decorative. Two rules hold it together:

     - Nothing depends on JavaScript to become visible. The scroll reveal only
       arms itself once the inline head script has stamped .pb-js on <html>, so
       a page without JS renders flat and complete rather than blank.
     - prefers-reduced-motion turns all of it off (see the block at the end),
       which means no keyframe may be the only thing that reveals content.     */

@keyframes pb-rise {
    from { opacity: 0; transform: translateY(1.5rem); filter: blur(10px); }
    to   { opacity: 1; transform: none;               filter: blur(0);    }
}
.pb-rise { animation: pb-rise 0.9s cubic-bezier(0.16, 0.84, 0.44, 1) both; }

/* The entrance order of the hero, roughly one beat per element. */
.pb-d1 { animation-delay: 0.05s; }
.pb-d2 { animation-delay: 0.20s; }
.pb-d3 { animation-delay: 0.35s; }
.pb-d4 { animation-delay: 0.50s; }
.pb-d5 { animation-delay: 0.65s; }
.pb-d6 { animation-delay: 0.80s; }

/* Ambient background orbs. Slower and less regular than animate-pulse, which
   reads as a blinking warning light at this size. */
@keyframes pb-drift {
    0%   { transform: translate3d(0, 0, 0)       scale(1);    opacity: 0.55; }
    50%  { transform: translate3d(4rem, 2rem, 0) scale(1.15); opacity: 0.9;  }
    100% { transform: translate3d(0, 0, 0)       scale(1);    opacity: 0.55; }
}
.pb-drift     { animation: pb-drift 18s ease-in-out infinite; }
.pb-drift-alt { animation: pb-drift 24s ease-in-out infinite reverse; }

/* Light sweeping over the wordmark. Needs background-clip: text, so the
   element must carry the gradient itself — hence the separate span. */
@keyframes pb-sheen {
    0%,  55% { background-position: 200% 0; }
    100%     { background-position: -60% 0; }
}
.pb-sheen {
    background-image: linear-gradient(100deg,
        #fff 42%, rgba(110, 231, 183, 0.95) 50%, #fff 58%);
    background-size: 250% 100%;
    background-position: 200% 0;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: pb-sheen 6s ease-in-out 1.2s infinite;
    /* The gradient only exists inside this span's box, and the hero's
       tracking-tighter subtracts its letter-spacing after the LAST glyph too —
       so the box ends a sliver before the final "b" does, and whatever part of
       the glyph lies outside is clipped to transparent: a bitten right edge.
       Padding extends the painted box past the glyph; the negative margin
       gives that room back, so the dot that follows does not move. */
    padding-right: .08em;
    margin-right: -.08em;
}

/* The emerald dot after "Plob": a slow breathing halo, not a blink. */
@keyframes pb-glow {
    0%, 100% { text-shadow: 0 0 18px rgba(16, 185, 129, 0.35); }
    50%      { text-shadow: 0 0 34px rgba(16, 185, 129, 0.85); }
}
.pb-glow { animation: pb-glow 3.5s ease-in-out infinite; }

/* Signal travelling along the top edge of the hero — the one literal nod to
   what the product does. */
@keyframes pb-beam {
    0%   { transform: translateX(-100%); }
    100% { transform: translateX(100%);  }
}
.pb-beam { animation: pb-beam 7s linear 1s infinite; }

@keyframes pb-blink {
    0%, 100% { opacity: 1;   transform: scale(1);   }
    50%      { opacity: 0.4; transform: scale(0.8); }
}
.pb-blink { animation: pb-blink 2s ease-in-out infinite; }

@keyframes pb-bob {
    0%, 100% { transform: translateY(0);      opacity: 0.45; }
    50%      { transform: translateY(0.4rem); opacity: 1;    }
}
.pb-bob { animation: pb-bob 2.4s ease-in-out infinite; }

/* Rotating word in the tagline. The track is a column of equal-height lines
   scrolled by whole lines; the last item repeats the first so the wrap back to
   translateY(0) is invisible. Heights are in em so it follows the font size. */
.pb-words {
    display: inline-block;
    height: 1.3em;
    overflow: hidden;
    vertical-align: bottom;
}
.pb-words-track {
    display: flex;
    flex-direction: column;
    animation: pb-words 10.5s cubic-bezier(0.8, 0, 0.2, 1) infinite;
}
.pb-words-track > span {
    height: 1.3em;
    line-height: 1.3em;
}
@keyframes pb-words {
    0%,   28% { transform: translateY(0);      }
    33%,  61% { transform: translateY(-1.3em); }
    66%,  94% { transform: translateY(-2.6em); }
    100%      { transform: translateY(-3.9em); }
}

/* Scroll reveal for the sections below the fold. Armed by .pb-js only, so the
   content is plainly visible when the script never runs. */
.pb-js .pb-reveal {
    opacity: 0;
    transform: translateY(1.75rem);
    transition:
        opacity 0.7s ease,
        transform 0.7s cubic-bezier(0.16, 0.84, 0.44, 1);
}
.pb-js .pb-reveal.pb-in {
    opacity: 1;
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    .pb-rise, .pb-drift, .pb-drift-alt, .pb-sheen, .pb-glow,
    .pb-beam, .pb-blink, .pb-bob, .pb-words-track {
        animation: none !important;
    }
    /* The sheen colours the text by clipping a gradient; without the animation
       it would sit frozen mid-sweep, so fall back to plain white. */
    .pb-sheen {
        background-image: none;
        -webkit-text-fill-color: #fff;
        color: #fff;
    }
    /* Only the first word of the rotator is shown. */
    .pb-words-track > span:not(:first-child) { display: none; }
    .pb-js .pb-reveal {
        opacity: 1;
        transform: none;
        transition: none;
    }
}

/* ── App sidebar ────────────────────────────────────────────────────────────
   The nav entries carried the same twelve Tailwind classes each, in blue —
   the app's accent is emerald everywhere else. One class here instead, so the
   hover, the icon colour and the active marker stay in step.                 */

.pb-nav-item {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    width: 100%;
    padding: 0.5rem 0.75rem;
    border-radius: 0.5rem;
    /* A <button> centres its text and an <a> does not; the rows are a mix of
       both, so say it once here. */
    text-align: left;
    font-size: 0.875rem;
    font-weight: 500;
    color: #374151;               /* gray-700 */
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

/* The marker grows out of the left edge rather than appearing whole, which
   keeps the row from jumping. */
.pb-nav-item::before {
    content: '';
    position: absolute;
    left: 0;
    top: 50%;
    width: 2px;
    height: 0;
    border-radius: 9999px;
    background-color: #10b981;    /* emerald-500 */
    transform: translateY(-50%);
    transition: height 0.15s ease;
}

.pb-nav-item:hover {
    background-color: #ecfdf5;    /* emerald-50  */
    color: #047857;               /* emerald-700 */
}
.pb-nav-item:hover::before { height: 1.25rem; }

/* Set by _setActiveNav (js/app.js) on the view currently on screen. */
.pb-nav-item.is-active {
    background-color: #ecfdf5;
    color: #047857;
    font-weight: 700;
}
.pb-nav-item.is-active::before { height: 1.25rem; }

/* A muted icon that lights up reads as one row with its label; a fixed width
   keeps the labels aligned whatever the glyph. */
.pb-nav-item > i {
    width: 1.25rem;
    text-align: center;
    color: #9ca3af;               /* gray-400 */
    transition: color 0.15s ease;
}
.pb-nav-item:hover > i,
.pb-nav-item.is-active > i { color: #10b981; }

.pb-nav-item.is-disabled {
    color: #9ca3af;
    cursor: not-allowed;
}
.pb-nav-item.is-disabled:hover {
    background-color: transparent;
    color: #9ca3af;
}
.pb-nav-item.is-disabled:hover::before { height: 0; }
.pb-nav-item.is-disabled:hover > i { color: #9ca3af; }

/* The profile card at the top of the sidebar. It is not a .pb-nav-item — it is
   a card with an avatar, not a nav row — but it does lead to a page now, so it
   answers to the same is-active mark _setActiveNav sets on the rest. */
[data-nav="profile"].is-active {
    border-color: #10b981;                                  /* emerald-500 */
    box-shadow: 0 0 0 1px #10b981, 0 1px 2px rgb(0 0 0 / 0.05);
}

/* ── Preferences: the theme picker ──────────────────────────────────────────
   Three cards showing the three choices at once, where the quick-settings menu
   had one pill you clicked until its label said what you wanted.              */

.pb-theme-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.375rem;
    padding: 0.875rem 0.5rem;
    border: 1px solid #e5e7eb;                              /* gray-200   */
    border-radius: 0.75rem;
    background-color: #f9fafb;                              /* gray-50    */
    font-size: 0.8125rem;
    font-weight: 600;
    color: #374151;                                         /* gray-700   */
    cursor: pointer;
    transition: border-color 0.15s ease, background-color 0.15s ease, color 0.15s ease;
}

.pb-theme-card > i {
    font-size: 1.125rem;
    color: #9ca3af;                                         /* gray-400   */
    transition: color 0.15s ease;
}

.pb-theme-card:hover {
    border-color: #6ee7b7;                                  /* emerald-300 */
    background-color: #ecfdf5;                              /* emerald-50  */
}

.pb-theme-card.is-active {
    border-color: #10b981;                                  /* emerald-500 */
    background-color: #ecfdf5;
    color: #047857;                                         /* emerald-700 */
    box-shadow: 0 0 0 1px #10b981;
}

.pb-theme-card:hover > i,
.pb-theme-card.is-active > i { color: #10b981; }

/* ── Profile: the danger zone ───────────────────────────────────────────────
   A <details>, so opening it needs no JavaScript. The row draws its own
   chevron; the native marker has to go, and list-style alone does not remove
   it in WebKit.                                                              */

#danger-zone > summary { list-style: none; }
#danger-zone > summary::-webkit-details-marker { display: none; }
#danger-zone > summary::marker { content: ''; }

/* Collapsed sidebar (lg:w-24): the labels are hidden, so centre what is left
   and drop the marker, which would sit against the edge of a narrow rail. */
.pb-sidebar-collapsed .pb-nav-item {
    justify-content: center;
    padding-left: 0.5rem;
    padding-right: 0.5rem;
}
.pb-sidebar-collapsed .pb-nav-item::before { height: 0 !important; }

/* ── Contacts ───────────────────────────────────────────────────────────────
   The rail of the Contacts tab: All contacts, the groups, and the history.
   Same idea as the sidebar above — one row, an icon, a count — but it is a
   filter rather than navigation, so it lives on its own class and keeps its
   marker on the left even when the sidebar next to it is collapsed.          */

.pb-contact-filter {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.5rem 0.625rem;
    border-radius: 0.5rem;
    font-size: 0.875rem;
    font-weight: 500;
    color: #374151;               /* gray-700 */
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.pb-contact-filter:hover {
    background-color: #ecfdf5;    /* emerald-50  */
    color: #047857;               /* emerald-700 */
}
.pb-contact-filter.is-active {
    background-color: #ecfdf5;
    color: #047857;
    font-weight: 700;
}
.pb-contact-filter > i {
    color: #9ca3af;               /* gray-400 */
    transition: color 0.15s ease;
}
.pb-contact-filter:hover > i,
.pb-contact-filter.is-active > i { color: #10b981; }

/* The count sits at the end of the row and must not push the name around, so
   it keeps a floor of its own rather than hugging one or three digits. */
.pb-contact-count {
    flex-shrink: 0;
    min-width: 1.5rem;
    padding: 0 0.375rem;
    border-radius: 9999px;
    background-color: #f3f4f6;    /* gray-100 */
    color: #6b7280;               /* gray-500 */
    font-size: 0.6875rem;
    font-weight: 700;
    line-height: 1.25rem;
    text-align: center;
}
.pb-contact-filter.is-active .pb-contact-count {
    background-color: #a7f3d0;    /* emerald-200 */
    color: #065f46;               /* emerald-800 */
}

/* ── In-app documentation ───────────────────────────────────────────────────
   The index doubles as a phone-sized chip row and a desktop sticky index, so
   the shared bits live here rather than in a long Tailwind class list.        */

.doc-toc-link {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 0.625rem;
    border-radius: 0.5rem;
    color: #374151;               /* gray-700  */
    white-space: nowrap;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.doc-toc-link:hover {
    background-color: #ecfdf5;    /* emerald-50  */
    color: #047857;               /* emerald-700 */
}
/* Set by _onDocSpy on the section currently under the top of the viewport. */
.doc-toc-link.is-active {
    background-color: #ecfdf5;
    color: #047857;
    font-weight: 700;
}
.doc-toc-link.is-active > i { color: #10b981; }

/* Filtered out by the search box. A class rather than an inline style so a
   section can be hidden and shown again without touching its own styling. */
.doc-hidden { display: none !important; }

/* Search hits, wrapped by _onDocSearch. */
.doc-mark {
    background-color: #a7f3d0;    /* emerald-200 */
    color: inherit;
    border-radius: 0.25rem;
    padding: 0 0.125rem;
}

/* ── File manager table ─────────────────────────────────────────────────────
   Below md the rows keep their phone layout (two wrapped lines) and none of
   this applies. From md up the row is a grid, and its columns are sized in rem
   rather than in twelfths: a share list squeezed between the folder tree and
   the details pane can be a few hundred pixels wide on a laptop, where a
   percentage-based Name column collapses to a couple of words.

   The column set follows the width the table actually gets, measured in
   _fmApplyLayout (js/file.js), which adds these classes to #fm-list as it
   shrinks — each one implies the previous:

     fm-hide-owner     Owner moves to the row's second line
     fm-hide-created   Created moves there too
     fm-fold-transfer  the transfer cell takes its own line, as on phones

   Nothing is ever lost, it only moves.                                      */

/* Never on phones: there the row already carries its own second line. */
.fm-inline { display: none; }

@media (min-width: 768px) {
    .fm-grid {
        grid-template-columns:
            minmax(0, 1fr)  /* name     */
            5.5rem          /* size     */
            5.5rem          /* owner    */
            6.5rem          /* created  */
            10.5rem         /* transfer */
            6rem;           /* actions  */
    }
    /* A cell may never widen its track — the name column would be the one to
       give way, and it is the one that matters — nor spill into its neighbour:
       the second line of the name cell is a row of nowrap items. */
    .fm-grid > * { min-width: 0; overflow: hidden; }
    /* Pinned, so the folded transfer cell below can span the whole row without
       pushing the buttons onto a line of their own. */
    .fm-cell-actions { grid-row: 1; grid-column: -2 / -1; }

    .fm-hide-owner .fm-grid {
        grid-template-columns: minmax(0, 1fr) 5.5rem 6.5rem 10.5rem 6rem;
    }
    .fm-hide-owner .fm-cell-owner   { display: none; }
    .fm-hide-owner .fm-inline-owner { display: block; }

    .fm-hide-created .fm-grid {
        grid-template-columns: minmax(0, 1fr) 5.5rem 10.5rem 6rem;
    }
    .fm-hide-created .fm-cell-created   { display: none; }
    .fm-hide-created .fm-inline-created { display: block; }

    .fm-fold-transfer .fm-grid {
        grid-template-columns: minmax(0, 1fr) 5.5rem 6rem;
        /* The folded cell is empty unless a transfer is running: no row gap, so
           an idle row stays as tall as it was. */
        row-gap: 0;
    }
    .fm-fold-transfer .fm-cell-transfer          { grid-column: 1 / -1; }
    .fm-fold-transfer .fm-speeds                 { display: none; }
    .fm-fold-transfer .fm-head .fm-cell-transfer { display: none; }
    .fm-fold-transfer .attempt-connect,
    .fm-fold-transfer .attempt-download          { padding-top: 0.25rem; }
}

/* ── Filing by drag ─────────────────────────────────────────────────────────
   A drag where nothing changes reads as "something is moving" and says nothing
   about where it may land — the folders it can land on are a narrow column of
   small text on the far side of the window, and they looked no different from
   the quick-access entries above them, which are not targets at all.

   So while a row is in the air, everything that cannot be dropped on steps
   back — the rows, the toolbar, the details pane, the quick-access group — and
   the folders are the only thing left at full strength.

   Three levels, not two. The dragged row is faded as well, only less than the
   rest of the list: left fully opaque it read as the one thing still in place,
   when it is the one thing that has been picked up. Half-there says lifted, and
   it still stands out from the rows around it.

   The opacity sits on each row rather than on their container: a child cannot
   climb back out of a parent's opacity.

   Toggled by _onFmDragStart / _onFmDragEnd (js/file.js).                     */

.fm-dragging #fm-quick,
.fm-dragging #fm-sticky-head,
.fm-dragging #fm-details,
.fm-dragging #fm-rows > .fm-row {
    opacity: 0.35;
    transition: opacity 0.15s ease;
}
.fm-dragging #fm-rows > .fm-row.fm-row-dragged { opacity: 0.6; }

/* Every folder is a target, so every folder says so. An inset ring rather than
   a border, which would move the label by a pixel the moment it appears. */
.fm-dragging .fm-tree-item[data-fm-folder-id] {
    background-color: #ffffff;
    box-shadow: inset 0 0 0 1px #a7f3d0;   /* emerald-200 */
}
/* The one under the pointer, told apart from the rest of them. */
.fm-dragging .fm-tree-item[data-fm-folder-id].fm-drop-target {
    box-shadow: inset 0 0 0 2px #10b981;   /* emerald-500 */
    color: #047857;                        /* emerald-700 */
    font-weight: 600;
}

/* The way back out, under the folder list. Its resting colours are in the
   markup; this is only what the pointer arriving on it changes. */
#fm-drop-out.is-over {
    border-color: #ef4444;                 /* red-500 */
    background-color: #fee2e2;             /* red-100 */
}

/* The docked side panes are rem-sized, like the rest of the app, so a 125% font
   size makes them 25% wider — on a laptop that is taken straight out of the
   table. Cap them against the viewport: at 100% the rem value always wins, and
   the cap only bites once the font preference would starve the list. */
@media (min-width: 1024px) {
    #fm-tree                   { width: min(15rem, 24vw); }
}
@media (min-width: 1280px) {
    #fm-tree                   { width: min(16rem, 24vw); }
    #fm-details:not(.hidden)   { width: min(20rem, 26vw); }
}
@media (min-width: 1536px) {
    #fm-details:not(.hidden)   { width: min(24rem, 26vw); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   PUBLIC PAGES — the shared vocabulary
   ═══════════════════════════════════════════════════════════════════════════

   The landing page grew one section at a time and each one invented its own
   measurements: five heading sizes, four card radii (rounded-3xl, 2.5rem, 3rem,
   2xl), two icon shapes, three card backgrounds, and section paddings that
   happened to match rather than agreeing to. Read from the top it does not look
   composed, it looks accumulated.

   So the recurring pieces are named once, here, and the markup asks for them by
   name. Nothing about the copy changes — every one of those sentences is a
   translation key — only the measurements it is wearing.

   These classes come from our own stylesheet, which loads BEFORE the Tailwind
   runtime injects its utilities: a utility of equal specificity therefore wins
   over one of these. Where a pb- class owns a property, the matching utility has
   to come off the element — otherwise the old value quietly stays.           */

/* ── Rhythm ──────────────────────────────────────────────────────────────── */

/* One vertical rhythm for every band of the page, a little tighter on a phone
   where 6rem of nothing is a scroll for no reason. */
.pb-band {
    position: relative;
    padding-top: 5rem;
    padding-bottom: 5rem;
}
@media (min-width: 768px) {
    .pb-band { padding-top: 7rem; padding-bottom: 7rem; }
}

/* The two surfaces the page alternates between, said once so the sequence is
   readable in the markup instead of being spelled out per section. */
.pb-band-light { background-color: #ffffff; }
.pb-band-tint  { background-color: #f8fafc; }   /* slate-50 */
.pb-band-dark  { background-color: #020617; }   /* slate-950 */

/* The hairline the hero already used where it meets the section below. Reused
   at every light/dark boundary, which is what makes the dark bands read as
   punctuation rather than as interruptions. */
.pb-edge::before,
.pb-edge::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    height: 1px;
    background: linear-gradient(to right, transparent, rgba(16, 185, 129, .5), transparent);
}
.pb-edge::before { top: 0; }
.pb-edge::after  { bottom: 0; }

/* ── Section headers ─────────────────────────────────────────────────────── */

.pb-head {
    max-width: 46rem;
    margin: 0 auto 4rem;
    text-align: center;
}
.pb-head-wide { max-width: 60rem; }

/* One scale for every section title. clamp() rather than a md: variant: the
   sizes were 3xl, 4xl and 4xl/5xl depending on the section, and a fluid value
   removes both the inconsistency and the breakpoint. */
.pb-h2 {
    margin: 0;
    font-size: clamp(1.875rem, 1.15rem + 2.4vw, 3rem);
    font-weight: 900;
    line-height: 1.1;
    letter-spacing: -.02em;
    color: #0f172a;
}
.pb-band-dark .pb-h2 { color: #ffffff; }

/* The short accent rule under every title. Small thing, and the single most
   effective one: it is the mark that says these bands belong to each other. */
.pb-rule {
    width: 3rem;
    height: 3px;
    margin: 1.25rem auto 0;
    border-radius: 999px;
    background: linear-gradient(to right, #34d399, #059669);
}
.pb-head .pb-rule + .pb-lede { margin-top: 1.5rem; }

/* One size, one colour, one measure for every standfirst. */
.pb-lede {
    margin: 1.5rem 0 0;
    font-size: 1.0625rem;
    line-height: 1.75;
    color: #64748b;                 /* slate-500 */
}
@media (min-width: 768px) {
    .pb-lede { font-size: 1.125rem; }
}
.pb-band-dark .pb-lede { color: #94a3b8; }

/* ── Cards ───────────────────────────────────────────────────────────────── */

.pb-grid {
    display: grid;
    gap: 1.5rem;
    grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 768px) {
    .pb-grid    { gap: 2rem; }
    .pb-grid-2  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .pb-grid-3  { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* One card. One radius, one padding, one border, one hover — where there were
   four of each. The lift on hover is small on purpose: a whole grid of cards
   jumping 8px is a fairground, not a page. */
.pb-card {
    position: relative;
    display: flex;
    flex-direction: column;
    padding: 2rem;
    border-radius: 1.5rem;
    background-color: #ffffff;
    border: 1px solid #e2e8f0;
    transition: transform .3s ease, box-shadow .3s ease, border-color .3s ease;
}
@media (min-width: 768px) {
    .pb-card { padding: 2.5rem; }
}
.pb-card:hover {
    transform: translateY(-3px);
    border-color: #6ee7b7;
    box-shadow: 0 18px 40px -18px rgba(15, 23, 42, .18);
}
/* On a white band the card needs the opposite surface to be seen at all. */
.pb-band-light .pb-card { background-color: #f8fafc; border-color: #eef2f7; }
.pb-band-light .pb-card:hover { background-color: #ffffff; }

/* The same card on a dark band. */
.pb-band-dark .pb-card {
    background-color: #0f172a;
    border-color: #1e2939;
}
.pb-band-dark .pb-card:hover {
    border-color: #047857;
    box-shadow: 0 18px 40px -18px rgba(0, 0, 0, .6);
}

/* The panel variant: one big card holding a whole section rather than a third
   of a row. Same family, one step up in radius. */
.pb-panel {
    border-radius: 2rem;
    background-color: #ffffff;
    border: 1px solid #e2e8f0;
    box-shadow: 0 30px 60px -30px rgba(15, 23, 42, .22);
}
@media (min-width: 768px) {
    .pb-panel { border-radius: 2.5rem; }
}

/* ── Card furniture ─────────────────────────────────────────────────────── */

/* One icon tile, where there were a 4rem rounded square and a 3.5rem circle
   doing the same job two sections apart. */
.pb-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 3.25rem;
    height: 3.25rem;
    margin-bottom: 1.5rem;
    border-radius: 1rem;
    font-size: 1.5rem;
    line-height: 1;
    background-color: #ffffff;
    border: 1px solid #e2e8f0;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
    transition: transform .3s ease;
}
.pb-card:hover .pb-icon { transform: scale(1.08); }
.pb-band-dark .pb-icon  { background-color: #020617; border-color: #1e2939; }

.pb-card-title {
    margin: 0 0 .75rem;
    font-size: 1.25rem;
    font-weight: 700;
    line-height: 1.3;
    color: #0f172a;
}
.pb-band-dark .pb-card-title { color: #ffffff; }

.pb-card-text {
    margin: 0;
    font-size: .9375rem;
    line-height: 1.7;
    color: #64748b;
}
.pb-band-dark .pb-card-text { color: #94a3b8; }

/* ── Numbered steps ─────────────────────────────────────────────────────── */

.pb-step-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 2rem;
    height: 2rem;
    margin-right: .875rem;
    border-radius: .75rem;
    background: linear-gradient(to bottom right, #34d399, #059669);
    color: #ffffff;
    font-size: .8125rem;
    font-weight: 900;
    box-shadow: 0 6px 14px -6px rgba(5, 150, 105, .8);
}

/* ── The dot grid ───────────────────────────────────────────────────────── */

/* The hero's texture, so a band that wants it does not restate the gradient.
   Faded towards the edges, as there, so it never meets a border. */
.pb-dots::before {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    opacity: .15;
    background-image: radial-gradient(currentColor 0.5px, transparent 0.5px);
    background-size: 28px 28px;
    -webkit-mask-image: radial-gradient(ellipse at center, black, transparent 70%);
    mask-image: radial-gradient(ellipse at center, black, transparent 70%);
}

/* ── The aside strip ────────────────────────────────────────────────────── */

/* One line of prose that belongs to neither of the sections it sits between —
   the third-party client note. Contained and bordered, so it reads as an aside
   rather than as something that fell out of a section. */
.pb-aside {
    max-width: 46rem;
    margin: 0 auto;
    padding: 1rem 1.5rem;
    border-radius: 999px;
    background-color: #ffffff;
    border: 1px solid #e2e8f0;
    font-size: .875rem;
    line-height: 1.6;
    color: #94a3b8;
    text-align: center;
}

/* ── Public pages: the plan sections ─────────────────────────────────────────
   Premium and Enterprise are the same pitch twice, once for a person and once
   for a team, and they were built as two unrelated components: a two-column
   band with a mockup against a full-width dark panel, a solid eyebrow pill
   against a translucent one, icon bullets against SVG ticks, a black button
   against a white one. Side by side on the landing page that reads as two
   designers who never met.

   One anatomy below, in two accents. Every element each of them already had
   keeps its place — eyebrow, title, standfirst, list, call to action — and the
   accent is the only thing that changes.                                     */

.pb-plan      { --pb-accent: #059669; --pb-accent-soft: #ecfdf5; }
.pb-plan-blue { --pb-accent: #2563eb; --pb-accent-soft: #eff6ff; }

/* The kicker above a title. One shape, tinted by the accent. */
.pb-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: 1.5rem;
    padding: .3rem .875rem;
    border-radius: 999px;
    background-color: var(--pb-accent-soft, #ecfdf5);
    border: 1px solid color-mix(in srgb, var(--pb-accent, #059669) 30%, transparent);
    color: var(--pb-accent, #059669);
    font-size: .6875rem;
    font-weight: 700;
    letter-spacing: .14em;
    text-transform: uppercase;
}
/* On a dark band the wash has to come from the accent itself, not from a light
   tint that would punch a hole in the surface. */
.pb-band-dark .pb-eyebrow {
    background-color: color-mix(in srgb, var(--pb-accent, #059669) 14%, transparent);
    border-color: color-mix(in srgb, var(--pb-accent, #059669) 32%, transparent);
    color: color-mix(in srgb, var(--pb-accent, #059669) 55%, #ffffff);
}

.pb-plan .pb-rule { background: linear-gradient(to right,
    color-mix(in srgb, var(--pb-accent) 55%, #ffffff), var(--pb-accent)); }

/* The feature list. Two columns from md up, because eight one-line items in a
   single column is a wall; the marker is the accent, whatever draws it. */
.pb-checklist {
    display: grid;
    gap: .875rem 2rem;
    grid-template-columns: minmax(0, 1fr);
    margin: 0 0 2.5rem;
    padding: 0;
    list-style: none;
    text-align: left;
}
@media (min-width: 768px) {
    .pb-checklist { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.pb-checklist > li {
    display: flex;
    align-items: flex-start;
    gap: .75rem;
    font-size: .9375rem;
    font-weight: 500;
    line-height: 1.6;
    color: #334155;                 /* slate-700 */
}
.pb-band-dark .pb-checklist > li { color: #cbd5e1; }
/* Both markers — an <i> glyph and an inline <svg> tick — land on the accent and
   on the same optical line, so the two lists finally look related. */
.pb-checklist > li > i,
.pb-checklist > li > svg {
    flex-shrink: 0;
    margin-top: .2rem;
    width: 1.125rem;
    color: var(--pb-accent, #059669);
}

/* The call to action and whatever sits beside it (a price, a platform note). */
.pb-cta-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 1rem 1.5rem;
}
.pb-cta-note {
    margin: 0;
    font-size: .9375rem;
    font-weight: 600;
    color: #64748b;
}
.pb-band-dark .pb-cta-note { color: #94a3b8; }

/* One button. It was black on one section and white on the other, hovering to
   two different colours; here it is the surface's opposite, hovering to the
   accent — the same gesture on a light band and on a dark one. */
.pb-btn {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    padding: .9rem 2rem;
    border-radius: 1rem;
    background-color: #020617;
    color: #ffffff;
    font-weight: 800;
    box-shadow: 0 14px 30px -14px rgba(2, 6, 23, .55);
    transition: background-color .3s ease, transform .3s ease, box-shadow .3s ease;
}
.pb-btn:hover {
    background-color: var(--pb-accent, #059669);
    transform: translateY(-2px);
    box-shadow: 0 18px 36px -14px color-mix(in srgb, var(--pb-accent, #059669) 60%, transparent);
}
.pb-band-dark .pb-btn { background-color: #ffffff; color: #020617; }
.pb-band-dark .pb-btn:hover { background-color: var(--pb-accent, #059669); color: #ffffff; }
.pb-btn > svg { width: 1.125rem; height: 1.125rem; transition: transform .3s ease; }
.pb-btn:hover > svg { transform: translateX(3px); }

/* The dark card a section uses to show the product rather than describe it. */
.pb-showcase {
    position: relative;
    padding: 1.75rem;
    border-radius: 1.5rem;
    background-color: #0f172a;
    border: 1px solid #1e2939;
    box-shadow: 0 30px 60px -30px rgba(2, 6, 23, .5);
}
.pb-showcase-label {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: 1.25rem;
    font-size: .6875rem;
    font-weight: 700;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: #64748b;
}
.pb-showcase-row {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .75rem;
    border-radius: .75rem;
    background-color: rgba(30, 41, 59, .7);
    border: 1px solid #334155;
    color: #ffffff;
    font-weight: 600;
    font-size: .9375rem;
}
.pb-showcase-row + .pb-showcase-row { margin-top: .75rem; }
.pb-showcase-row > i { color: var(--pb-accent, #059669); }
.pb-showcase-meta {
    margin-left: auto;
    font-size: .75rem;
    font-weight: 500;
    color: #94a3b8;
}

/* ── Public pages: a dark card inside a light band ───────────────────────────
   The Plob Desktop block had six bespoke pieces of its own — its own pill, its
   own uppercase heading with an inline icon, character bullets in a hand-rolled
   two-column grid, its own button, its own platform note — and it stacked three
   surfaces to hold them: a tinted band, a white panel, and a dark strip inside
   that. These few rules are what the shared pieces need in order to work on the
   dark card, so the block can use them and drop its own.                      */

/* A checklist on the dark card cannot keep the slate-700 it wears on a light
   band. Scoped to the showcase, which is the only dark context inside a light
   section. */
.pb-showcase .pb-checklist > li { color: #cbd5e1; }

/* This list marks its items with a character rather than an icon, so the marker
   is a leading <span>. Only ever matches that: an icon-marked checklist has an
   <i> or an <svg> as its first child, which this selector cannot reach. */
.pb-checklist > li > span:first-child { flex-shrink: 0; }

/* The label of a showcase, when it names a feature list rather than a mock-up:
   an icon sized by the label instead of by the markup it sits in. */
.pb-showcase-label > svg { width: 1rem; height: 1rem; flex-shrink: 0; }

/* A download button: the arrow belongs going down, not sideways. */
.pb-btn-down:hover > svg { transform: translateY(2px); }
