/**
 * furloop-playful.css — the shared "playful" component layer.
 *
 * Sticker-sheet neo-brutalism, from design_language_playful.md: warm brown-black
 * ink, 2px outlines, hard un-blurred offset shadows, small alternating tilts,
 * Baloo 2 for display type, uppercase mono for every label, one loud orange for
 * state.
 *
 * WHY THIS FILE EXISTS
 * Three modules — settings, orders, customers — each grew their own copy of the
 * same vocabulary, written by three sessions that never saw each other's code.
 * They converged on remarkably nearly the same thing: every colour token, every
 * shadow offset, the 14px radius, both easing curves and the whole label voice
 * came out byte-identical three times over. But 62 selectors were defined twice
 * and 57 declarations had drifted apart, mostly by a pixel or two. A primary
 * button was 42px tall with 9px 18px of padding under settings and 40px tall
 * with 8px 16px under orders. That is not a design decision anybody made; it is
 * what happens when one idea is written down three times.
 *
 * So the vocabulary is defined here, once, and the three module sheets keep only
 * what is genuinely theirs.
 *
 * WHO LOADS THIS, AND IN WHAT ORDER
 * Shared layer first, module sheet second, section sheet last. Always.
 *
 *   settings   includes/settings_playful.php emits, in this order:
 *                furloop-settings-pages.css   (layout — grids, spacing, max-width)
 *                furloop-playful.css          (this file)
 *                furloop-settings-playful.css (the settings remainder)
 *
 *   orders     views/order/_styles_playful.php  then  its section sheet
 *                (_styles_playful_lists.php / _bulk_sell.php / _edit_order.php)
 *
 *   customers  views/customer/_styles_playful.php  then  its section sheet
 *                (_styles_playful_list.php / _styles_playful_edit.php)
 *
 * The order is not a convenience. Several rules in each module sheet score
 * exactly what a rule here scores and win the tie on source order alone — that
 * is the mechanism the whole split relies on, and it is why no !important in
 * this file may ever be added casually. An !important here cannot be answered by
 * a module loading second; it can only be answered by another !important, which
 * is how a shared layer turns into a fight.
 *
 * THE OPT-IN
 * A container opts in by carrying `fl-playful` ALONGSIDE its existing marker,
 * never instead of it:
 *
 *   <div class="pc-container furloop-settings-page fl-playful">
 *   <div class="pc-container fl-ord-page fl-ord-list fl-playful">
 *   <div class="pc-container fl-cus-page fl-cus-list fl-playful">
 *
 * A page without the class is untouched by every rule below. That matters: four
 * views under views/order/ (abandoned_orders, archive_orders, add_order,
 * bulk_edit_order) run the same theme and the same .card/.btn/.table markup and
 * are deliberately unskinned. They are the live proof that the gate holds, and
 * they make good control pages — anything that leaks past `.fl-playful` shows up
 * on them first.
 *
 * SCOPING — (0,3,1), and why exactly that
 * Every selector group below begins `body:has(.fl-playful)`. Components then take
 * the descendant form:
 *
 *   body:has(.fl-playful) .fl-playful X
 *
 * `:has()` takes the specificity of its most specific argument, so
 * `body:has(.fl-playful)` contributes (0,1,1); the second class step brings the
 * whole thing to (0,3,1) for a single-class X.
 *
 * (0,3,1) is not a taste. It is the weight the orders and customers sheets
 * already carry, and those sheets document specific theme rules they had to
 * outrank to work at all:
 *
 *   style.css:30159      .table thead th { padding: 1rem 1rem !important }
 *   style.css:3228       .btn:focus-visible { outline: 0 }
 *   includes/footer.php  several !important rules on the datatable chrome
 *
 * Scoping this layer any lower — `:is(...) X` at (0,2,0), say — would make it
 * lose fights the orders core currently wins, on pages nobody would think to
 * re-check. So the settings remainder rises to match: it is rescoped to
 * `body:has(.fl-playful) .furloop-settings-page X`, also (0,3,1), and keeps
 * loading after this file so its ties still go to the module.
 *
 * There is exactly ONE place the (0,3,1) descendant form is not met, and it is
 * marked where it happens:
 *
 *   1. `.fl-playful-modal` — Bootstrap can move a modal to the end of <body>,
 *      outside the container. It is written `body:has(.fl-playful)
 *      .fl-playful-modal X`, which is still exactly (0,3,1) and still matches
 *      after the move.
 *
 * An earlier draft of this header claimed a second exception, for `.pc-content`,
 * on the grounds that it is an ANCESTOR of the marker. That was simply wrong:
 * on all 24 page containers `.pc-content` opens immediately INSIDE the
 * `fl-playful` element (change_password.php:9/:10, views/customers.php:55/:56,
 * views/order/orders.php:7/:9, views/location.php:13/:15, and so on for the
 * rest — each of those files contains exactly one `.pc-content`). The rule is
 * written in the ordinary (0,3,1) form like everything else. A false constraint
 * left in a header is worse than no header, because the next edit reasons from
 * it.
 *
 * WHAT THIS FILE DELIBERATELY DOES NOT COVER
 *   - The dashboard shell. `.pc-sidebar`, `.pc-header`, the top bar, the nav,
 *     the footer, the theme-customiser offcanvas. All three module skins leave
 *     the shell alone on purpose, because it reaches every other page in the
 *     product, and a re-skin that lands on five screens out of two hundred looks
 *     like a rendering fault rather than a decision. Anchoring to `.fl-playful`
 *     — a sibling of `.pc-sidebar`, not an ancestor — is what puts it out of
 *     reach.
 *   - Module LAYOUT. No flex skeletons, no grids, no page-head geometry, no
 *     spacing systems. Settings' layout lives in furloop-settings-pages.css and
 *     is not moving; orders and customers carry theirs inline. A skin that
 *     travels without the layout it skins is not shareable.
 *   - Anything named `.furloop-*`, `.fl-ord-*` or `.fl-cus-*`. Those are module
 *     class names. Sharing a component whose class exists in one module's markup
 *     only is not sharing, it is shipping dead weight to the other two — and in
 *     settings' case it would also mean overriding furloop-settings-pages.css at
 *     (0,3,1), which is the fastest way to fail the zero-diff invariant.
 *   - The link policy (§3.5's blanket underline). Settings applies it behind a
 *     long `:not()` guard; orders and customers both refuse it BY NAME, because
 *     edit_order and edit_customer each carry dozens of anchors acting as
 *     buttons and underlining all of them is noise. That is a genuine
 *     disagreement about what the pages are, not a value that drifted, so it
 *     stays with each module.
 *   - The full simple-datatables chrome. See the datatables note in the tables
 *     section — only the subset settings itself declares is shared.
 *   - Cascade trap #3 (Font Awesome and Summernote icon fonts). Those exemptions
 *     answer `*::before { font-family: inherit }` in furloop-settings-pages.css,
 *     a sheet only settings loads. Hoisting them would pin a family on two
 *     modules where it currently inherits correctly. Worse, the tempting
 *     "solution" to trap #1 — a universal `* { font-family: inherit }` here —
 *     would break the datatable search magnifier on the orders, customers AND
 *     location pages at once: it is a `::before` whose glyph comes from
 *     `font-family: "Font Awesome 5 Free"` at includes/footer.php:89, declared
 *     without !important. Tabler's `.ti` glyphs survive such a reset
 *     (tabler-icons.min.css carries !important); Font Awesome's do not.
 *
 * CANONICAL VALUES — settings won
 * Where the three sheets disagreed, the SETTINGS value is the one written below.
 * It is the oldest of the three and covers 22 pages against orders' 5 and
 * customers' 2, so it is both the best-tested and the cheapest to keep. The
 * concrete headline: a primary button is 42px tall with 9px 18px of padding
 * everywhere now. Every button on the orders and customers pages grows 2px
 * taller and 2px wider on each side. That shift is intended — it is the whole
 * point of the exercise — and every place one value displaced another is named
 * in a comment right where it happens, with both numbers, so the next reader can
 * see that a choice was made and what it cost.
 *
 * THE REGRESSION CONTRACT THIS FILE IS WRITTEN AGAINST
 * Settings pages must show ZERO computed-style change. The split is meant to be
 * lossless for them; any settings diff is a bug. Orders and customers should
 * show only the deltas the canonical values explain. Everything below is written
 * with that asymmetry in mind, which is why several rules that look obviously
 * generic were left behind: `.btn { text-transform: lowercase }` would lowercase
 * every button on 22 settings pages, `.card { transform: rotate(-0.5deg) }` would
 * tilt the entire page shell on six plain-Bootstrap settings screens, and
 * `.form-label { display: block }` would change the display of every form label
 * in the product's settings area. Each of those is one property inside an
 * otherwise-identical rule, which is exactly how this kind of migration goes
 * wrong quietly.
 *
 * TWO CASCADE TRAPS EVERY RULE HERE IS WRITTEN AROUND
 *   1. includes/header.php declares `* { font-family: 'Poppins' }` in an inline
 *      <style>. A universal selector does not merely win on specificity — it
 *      MATCHES every element and sets the property directly on each one, so
 *      inheritance never gets a turn. Setting a font on the container paints
 *      exactly zero descendants. Every component below therefore names its own
 *      `font-family`. There is no shortcut, and the one that looks like a
 *      shortcut is the magnifier bug described above.
 *   2. The theme colours `label` and `h1`–`h6` at ELEMENT-selector specificity
 *      (style.css:18940 paints headings with `--pc-heading-color`, the old
 *      #01017C indigo). A bare class cannot beat an element rule that sets the
 *      property directly, so ink has to be restated rather than inherited.
 *
 * AND ONE SHAPE OF BUG WORTH NAMING BEFORE IT HAPPENS AGAIN
 * A `padding` SHORTHAND resets the clearances that absolutely-positioned
 * prefixes and background icons depend on. Two bugs of exactly that shape were
 * fixed recently — a currency prefix rendering on top of a price, and a select
 * caret sitting over its own value. This file sets `padding` on controls and on
 * buttons. Every module or page rule that reserves room for an adornment must
 * therefore be restated at (0,3,1) or higher, or land later in the cascade. The
 * module sheets load after this one, so theirs win for free; page-level <style>
 * blocks do NOT, because specificity is decided before source order and most of
 * them sit at (0,2,0). The known sites are listed beside the control rules.
 *
 * Light-only, on purpose. There is no `prefers-color-scheme` rule anywhere in
 * the dashboard and a half-built dark mode is worse than none; §2.4 agrees.
 *
 * THE BALOO 2 <link> IS NOT EMITTED HERE. A stylesheet cannot emit one. It stays
 * with each module's existing PHP emitter (includes/settings_playful.php:28,
 * views/order/_styles_playful.php, views/customer/_styles_playful.php), exactly
 * once per page. Keep the href byte-identical across all of them or returning
 * users pay for a second font request:
 *   https://fonts.googleapis.com/css2?family=Baloo+2:wght@700;800&display=swap
 *
 * INTENDED NEXT CONSUMERS, NOT YET WIRED: views/consignment/_styles_playful.php,
 * views/pos/_styles.php, views/help/_styles.php and onboarding.php's own sheet
 * all run this language from their own copies. None of them is opted in here —
 * they have no captured baseline, so adopting the canonical values there would
 * produce deltas nobody could check. onboarding.php in particular must NOT gain
 * `fl-playful`: it renders its own <html> and deliberately avoids the dashboard
 * stylesheets because the sidebar is the thing it is configuring.
 */

/* ================================================================ tokens === */
/*
 * Declared on the BODY, not on the container.
 *
 * Custom properties inherit downwards only. A token declared on the page
 * container is invisible to any rule that paints behind it — a `body`
 * background rule would silently fall back to its initial value and the page
 * would keep the theme's grey-blue #F4F7FA. `body:has(.fl-playful)` still
 * matches only an opted-in screen, and everything inside inherits from here.
 *
 * The `body:has()` form is also what lets the two modals and Summernote's
 * injected toolbar resolve `var(--pl-*)` at all: they are rendered outside the
 * container, so a token declared on the container would leave every colour in
 * them unresolved.
 *
 * This is the UNION of the three modules' token sets. Four tokens existed in one
 * or two sheets and not the others; all four are brought across, because a token
 * costs nothing where it is unused and a hand-copied literal is exactly how a
 * value drifts. `--pl-hair` came from settings; `--pl-wash` and `--pl-measure`
 * from orders and customers; `--pl-rule` is the second, translucent hairline
 * that orders and customers each hand-wrote in four places.
 */
body:has(.fl-playful) {
    /* Surfaces — §2.1, with the white-paper substitution.
       DEPARTURE, and a standing house decision: paper is white, not §2.1's warm
       cream. The system survives it because what defines a card is the 2px
       outline and the stamped shadow, not the fill; warmth is carried by the
       ink, the band tone and the pastels. To try the cream the document actually
       calls for, change --pl-paper and --pl-card to #fff4dd and #fffdf7. */
    --pl-paper:       #ffffff;
    --pl-card:        #ffffff;
    --pl-band:        #f9f3e6;   /* inner panels, table heads, quiet fills */
    --pl-watch-paper: #fff0bd;   /* the butter alert surface */

    /* Ink — text, every border, every shadow (§2.3). Warm brown-black, so the
       palette does not drift grey. */
    --pl-ink:         #26201a;
    --pl-ink-2:       #5c5347;
    --pl-ink-3:       #877b6b;

    /* The one loud colour, and the decorative pastels (§2.2).
       DEPARTURE: §2.1 puts the band tone "one step darker than paper". Against
       white, the document's #f1e3c4 reads as tan, so the band gets its own
       lighter warm cream above and #f1e3c4 stays as --pl-empty for the small
       chips and hairlines where the deeper tone still works. */
    --pl-accent:      #ff5c2b;   /* THE orange — state, and nothing else */
    --pl-sun:         #ffd84d;
    --pl-sun-hi:      #ffe070;
    --pl-sky:         #a5dcff;
    --pl-sky-hi:      #c2e7ff;   /* was hand-written once, in the success hover */
    --pl-rose:        #ffb9cc;
    --pl-empty:       #f1e3c4;   /* the deeper cream, for chips and hairlines */
    --pl-olive:       #675015;   /* the only small-text colour on butter */

    /* Derived fills. --pl-row-hover is the table row wash. Settings wrote the
       literal #fff7e2; orders and customers tokenised a DIFFERENT value,
       rgba(255,216,77,.26), which composites over white to roughly #fff5d1.
       Settings wins, so order and customer tables hover very slightly warmer and
       more opaque than before. --pl-wash is kept because both module sheets
       still drive Bootstrap's --bs-table-hover-bg through it, and because a
       translucent tint and a flat fill are not interchangeable over a coloured
       row. */
    --pl-row-hover:   #fff7e2;
    --pl-wash:        rgba(255, 216, 77, .26);

    /* Shape — §4. Border and shadow always travel together (§12); a shadow
       without an outline is the most common way this language comes out looking
       like a filter. --pl-rule is the softer hairline the two ledger modules use
       between table rows, distinct from --pl-hair's solid ink. */
    --pl-border:      2px solid var(--pl-ink);
    --pl-hair:        1.5px solid var(--pl-ink);
    --pl-rule:        1.5px solid rgba(38, 32, 26, .18);
    --pl-shadow:      4px 4px 0 var(--pl-ink);
    --pl-shadow-sm:   3px 3px 0 var(--pl-ink);
    --pl-shadow-xs:   2px 2px 0 var(--pl-ink);
    --pl-shadow-hero: 6px 6px 0 var(--pl-ink);
    --pl-radius:      14px;

    /* §3.4 / §10 — the measure. Every run of body copy caps at 58ch, roughly 11
       words a line; past that the eye loses the return sweep. A token rather
       than a hand-copied `58ch` because four sheets need it and the ones that
       wrote it out by hand are exactly how a value drifts. Prose only — a
       nine-column ledger, a form row and a status line are not paragraphs. */
    --pl-measure:     58ch;

    /* Type — §3.1.
       Two things are load-bearing in these two stacks and neither is obvious.
       The CJK fallbacks come from settings and must stay: the shop-language page
       lists 中文（简体）and 日本語 as options and Baloo 2 covers neither. The
       rounded fallback comes from orders and customers and must also stay: the
       Baloo 2 <link> is discovered mid-body on those pages, so the first paint
       may not have the face yet, and without a rounded fallback that first paint
       is Times. Computed font-family is compared as a STRING, so editing either
       of these values is a settings diff even when it renders identically. */
    --pl-display: "Baloo 2", "Arial Rounded MT Bold", system-ui, "PingFang SC",
                  "Hiragino Sans", "Noto Sans CJK SC", sans-serif;
    --pl-body:    system-ui, -apple-system, "Segoe UI", "PingFang SC",
                  "Hiragino Sans", "Noto Sans CJK SC", sans-serif;
    --pl-mono:    ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;

    /* Easing — §8.1. Spring for anything bouncy, decelerate for entrances. */
    --pl-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
    --pl-decel:  cubic-bezier(0.16, 1, 0.3, 1);
}

/* ============================================================ the paper ==== */
/*
 * NOT AN EXCEPTION, though it used to be written as one. `.pc-content` is a
 * CHILD of the marker on every one of the 24 containers — the `fl-playful`
 * element is `.pc-container` and `.pc-content` opens immediately inside it — so
 * the ordinary `body:has(.fl-playful) .fl-playful X` form reaches it and the
 * file has no unanchored component rule. Nothing else declares a background on
 * `.pc-content` (style.css:21613 sets padding only, and
 * furloop-settings-pages.css declares no rule for it), so this is uncontested
 * either way; the point of anchoring it is that the (0,3,1) invariant holds
 * without an exception to remember, and that the rule cannot reach a
 * `.pc-content` outside the marker if one is ever added.
 *
 * `.pc-container` is deliberately NOT included, even though orders and customers
 * both clear it. `.pc-container` IS the marker element, and settings PAINTS it
 * (`background: var(--pl-paper)` on the container, below) where orders and
 * customers make it transparent and paint <body> instead. A shared
 * `.pc-container { background: transparent }` would un-paint every settings
 * wrapper. Both halves of that disagreement — the body paint and the container
 * clear — stay in the orders and customers sheets, where they travel together.
 */
body:has(.fl-playful) .fl-playful .pc-content { background: transparent; }

/*
 * Base type for the content region. Note this rule's subject is the container
 * ITSELF, so it scores (0,2,1), one class weaker than every component below —
 * that is inherent to the scoping form, not an oversight.
 *
 * Nothing inherits any of this: see cascade trap #1 in the header. It is still
 * the correct base for the handful of elements the theme's universal rule does
 * not reach, and it documents the intent.
 *
 * The `background` declaration is settings' (it paints the wrapper white and
 * leaves <body> at the theme's #F4F7FA). Orders and customers restate
 * `background: transparent` on `.pc-container` from their own sheets, which
 * score the same (0,2,1) and load second, so they keep winning it. If either
 * module sheet ever drops that line, its container turns white — which on a
 * white-painted body is invisible, but it is still a change.
 */
body:has(.fl-playful) .fl-playful {
    background: var(--pl-paper);
    color: var(--pl-ink);
    font-family: var(--pl-body);
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
}

/* ================================================================== type === */
/*
 * Cascade trap #2. style.css:18940 colours h1–h6 directly with
 * `--pc-heading-color` (#01017C indigo) at element-selector specificity, so
 * headings inherit nothing. Restate the ink or every title on the page stays in
 * the old palette while everything around it is #26201a. All three modules hit
 * this wall independently; so did the POS skin, at views/pos/_styles.php:85.
 *
 * RESOLVED: settings set five properties here, orders and customers set colour
 * ONLY. Settings wins, so every bare heading on the five order pages and the two
 * customer pages additionally picks up Baloo 2 / 800 / -0.01em / balanced
 * wrapping. This is the largest single typographic change those modules take
 * from the refactor, and it is the delta most likely to be mistaken for a bug in
 * the probe — a bare `<h4 class="card-title">` on an orders list page visibly
 * changes family.
 */
body:has(.fl-playful) .fl-playful h1,
body:has(.fl-playful) .fl-playful h2,
body:has(.fl-playful) .fl-playful h3,
body:has(.fl-playful) .fl-playful h4,
body:has(.fl-playful) .fl-playful h5,
body:has(.fl-playful) .fl-playful h6 {
    color: var(--pl-ink);
    font-family: var(--pl-display);
    font-weight: 800;
    letter-spacing: -0.01em;
    text-wrap: balance;
}

/*
 * Cascade trap #2 again, and the one rule in this file whose SPECIFICITY is
 * engineered rather than inherited from the contract.
 *
 * The theme colours `label` in the old indigo at element-selector specificity,
 * which a bare class cannot beat — so any label the page does not explicitly
 * paint keeps the previous palette's purple. Settings restated it at (0,1,1),
 * deliberately BELOW `.form-label` and the other label classes at (0,2,0), so
 * the label-voice rule further down still wins the colour.
 *
 * Written the obvious way here — `body:has(.fl-playful) .fl-playful label` — it
 * would score (0,3,2) and BEAT the shared `.form-label` at (0,3,1). Every form
 * label in the product's settings area would flip from --pl-ink-2 to --pl-ink,
 * silently, and source order could not fix it because specificity is decided
 * first. That is a settings regression hiding inside a lossless move.
 *
 * `:where()` contributes zero specificity to its argument, so this lands at
 * (0,2,1): above the theme's element rule, below every label class, and below
 * `.form-group > label:not(.form-check-label)` too. The ordering settings
 * intended is preserved exactly. `:where()` is supported everywhere `:has()` is,
 * and this file already requires `:has()`.
 *
 * Orders and customers never answered this trap at all, so any unclassed
 * <label> on those seven pages is still indigo today and turns ink here. That is
 * a real computed-colour delta and a fix, not a regression. Their section sheets
 * had patched two instances by hand; a third would have been missed.
 */
body:has(.fl-playful) .fl-playful :where(label) { color: var(--pl-ink); }

/* §3.4 — tabular numerals anywhere digits stack or update, so nothing jitters.
   `.fl-num` is already an fl-prefixed shared utility rather than any one
   module's vocabulary. Order and customer tables gain tabular figures here,
   which is small, welcome and reportable. */
body:has(.fl-playful) .fl-playful .table td,
body:has(.fl-playful) .fl-playful .table th,
body:has(.fl-playful) .fl-playful input[type="number"],
body:has(.fl-playful) .fl-playful .fl-num { font-variant-numeric: tabular-nums; }

/* §10 — every focusable thing gets a visible ring, offset far enough to clear
   the stamped shadow. The accent rather than ink, because ink is already the
   border colour and would read as a thicker border rather than as focus.
   This is one of the rules that must keep outranking the theme's `outline: 0`
   (style.css:3228); at (0,3,1) it does so more comfortably than settings' old
   (0,2,0) did. Note the orders sheet restates the ring on the datatable pager at
   (0,4,2) because that block's own `:focus` selector outranks this one — that
   arithmetic still holds, because this rule keeps exactly (0,3,1) here. */
body:has(.fl-playful) .fl-playful :focus-visible {
    outline: 3px solid var(--pl-accent);
    outline-offset: 3px;
}

/* =============================================================== cards ===== */
/*
 * §4: a raised surface is an outline plus a hard offset shadow, and they always
 * travel together (§12). `.card` is styled by name because six settings pages
 * are plain Bootstrap and never got the furloop components — without it they
 * fall straight through to the theme and stay in the old palette.
 *
 * §12's principle, recorded here because it is the rule three separate
 * components already follow and the next one will need it: a raised surface goes
 * directly on the paper, so a plate INSIDE a card takes the band fill rather
 * than a second stamped shadow. That is why settings' toggle cards, get_paid's
 * payment plates and orders' inner panels are all band-filled and shadow-xs at
 * most.
 *
 * NOT SHARED, and this one is a genuine conflict rather than a value: the card
 * TILT. Orders and customers rotate `.card` itself by ±0.5deg on :nth-child.
 * Settings refuses that by name — "a bare `.card` is the whole page shell on the
 * legacy screens, and a tilted page is not the same idea as a tilted card" — and
 * tilts only its own `.furloop-settings-card`. Promoting the tilt would rotate
 * the entire page shell on six plain-Bootstrap settings screens. It stays with
 * orders and customers.
 *
 * HAZARD for the settings migration, called out here because this is where the
 * declaration lives: `margin-bottom: 26px` rises from (0,2,0) to (0,3,1) and
 * will now beat views/get_paid.php:36's `.furloop-settings-page
 * .payment-method-card { margin-bottom: 0 }` (0,2,0), which today wins on source
 * order. That page rule must be restated at (0,3,1) or every payment card gains
 * 26px inside a grid that already spaces them. Leaving margin-bottom OUT of this
 * rule is not an option — it would strip 26px from every plain `.card` on 22
 * settings pages, which is a much larger diff.
 *
 * RESOLVED: settings sets `margin-bottom: 26px`; orders and customers set none.
 * Settings wins, so every `.card` on the five order pages and the two customer
 * pages gains a 26px bottom margin.
 */
body:has(.fl-playful) .fl-playful .card {
    background: var(--pl-card);
    border: var(--pl-border);
    border-radius: var(--pl-radius);
    box-shadow: var(--pl-shadow);
    margin-bottom: 26px;
}

/* §12 — don't stack card on card. A nested one steps down instead of repeating
   the same weight.
   RESOLVED: orders and customers killed the shadow outright and thinned the
   border to 1.5px; settings steps the shadow down to --pl-shadow-xs, adds a 16px
   bottom margin and leaves the border alone. Settings wins, so a nested card on
   an order or customer page gains a 2px offset shadow where it had none, keeps
   its 2px border, and gains 16px of bottom margin. */
body:has(.fl-playful) .fl-playful .card .card {
    box-shadow: var(--pl-shadow-xs);
    margin-bottom: 16px;
}

/* The inner curve. `- 2px` is the border width: the header band sits INSIDE the
   outline, so it needs the inner radius, not the outer, or the corner shows a
   sliver of card through the band. Expressed as a calc() off the radius token so
   there is one source for the number; customers hardcoded 12px, which happens to
   equal 14 - 2 and would stop being true the moment --pl-radius moved. */
body:has(.fl-playful) .fl-playful .card > .card-header:first-child {
    border-radius: calc(var(--pl-radius) - 2px) calc(var(--pl-radius) - 2px) 0 0;
}
body:has(.fl-playful) .fl-playful .card > .card-footer:last-child {
    border-radius: 0 0 calc(var(--pl-radius) - 2px) calc(var(--pl-radius) - 2px);
}

body:has(.fl-playful) .fl-playful .card-header {
    background: var(--pl-band);
    border-bottom: var(--pl-border);
    color: var(--pl-ink);
}
/* `.card-label` is Able Pro theme vocabulary, used product-wide rather than by
   any one module, so it belongs with the header treatment. */
body:has(.fl-playful) .fl-playful .card-header h1,
body:has(.fl-playful) .fl-playful .card-header h2,
body:has(.fl-playful) .fl-playful .card-header h3,
body:has(.fl-playful) .fl-playful .card-header h4,
body:has(.fl-playful) .fl-playful .card-header h5,
body:has(.fl-playful) .fl-playful .card-header .card-label {
    color: var(--pl-ink);
    font-family: var(--pl-display);
    font-weight: 800;
    margin: 0;
}
body:has(.fl-playful) .fl-playful .card-footer {
    background: var(--pl-band);
    border-top: var(--pl-border);
}

body:has(.fl-playful) .fl-playful .list-group-item {
    background: transparent;
    border-color: var(--pl-empty);
    color: var(--pl-ink);
}

/* ============================================================ controls ===== */
/*
 * §4.3, the single strongest rule in the language: interactive is a capsule,
 * static is a rounded rect. A user can tell what is clickable from corner radius
 * alone. §6.2 then makes inputs and buttons share a height, a radius, a border
 * and a shadow, so a filter row reads as one control group rather than as a form
 * sitting next to a button.
 */

/*
 * §3.2's inversion, and the single most recognisable piece of the vocabulary:
 * label type takes heavy positive tracking where display type takes negative.
 * All three modules reproduced it and all three landed on the same seven values
 * — mono, 11px, 700, 0.08em, uppercase, --pl-ink-2, 7px of bottom margin. That
 * agreement is the best evidence in the codebase that this vocabulary is real.
 *
 * NOT SHARED: customers and orders also set `display: block` here. Bootstrap's
 * own `.form-label` sets no display, so a `<label class="form-label">` on a
 * settings page computes to `display: inline` today and promoting the block
 * would change all 22 of them. It stays in those two module sheets.
 */
body:has(.fl-playful) .fl-playful .form-label,
body:has(.fl-playful) .fl-playful .form-group > label:not(.form-check-label),
body:has(.fl-playful) .fl-playful .col-form-label {
    color: var(--pl-ink-2);
    font-family: var(--pl-mono);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    margin-bottom: 7px;
    text-transform: uppercase;
}

/*
 * §6.2's capsule, and the highest-risk rule in this file.
 *
 * RESOLVED — the second headline shift: settings is min-height 42px / padding
 * 9px 16px; orders and customers are 40px / 8px 15px. Settings wins, so every
 * input and select on those seven pages grows 2px taller and gains a pixel of
 * padding on each side, matching the button change exactly. That matching is the
 * point — §6.2 only works if the two numbers stay equal, so if either is ever
 * changed, both must be.
 *
 * RESOLVED — mechanism: orders and customers wrote `font: 600 14px/1.3
 * var(--pl-body)`, a SHORTHAND. Settings used longhands. Longhands win, and this
 * is not a style preference: the `font` shorthand also resets font-style,
 * font-variant and font-stretch to their initial values, which would wipe the
 * tabular numerals off `input[type="number"]` on all 22 settings pages — a
 * zero-diff failure no visual review would ever catch. It also sets a
 * line-height, and settings' longhands set none, so order and customer controls
 * move from line-height 1.3 to the inherited 1.55. If that matters on a specific
 * control, restate `line-height: 1.3` in the module sheet.
 *
 * ***PADDING-SHORTHAND TRAP.*** `padding: 9px 16px` at (0,3,1) resets clearance
 * that lower-specificity rules reserve for adornments and which today win on
 * source order alone. Every one of these must be restated at (0,3,1) or higher
 * in its own file as part of this migration:
 *
 *   change_password.php:152   .furloop-settings-page .position-relative >
 *                             .form-control { padding-right: 40px }   (0,2,0)
 *                             — reserves room for the show/hide eye toggle.
 *                             Unanswered, the eye renders on top of the password.
 *   change_password.php:180   the 36px variant of the same, below 576px.
 *   get_paid.php:195          .payment-method-settings select.form-control
 *                             { padding-right: 38px }                 (0,2,1)
 *                             — the country chevron lands on the country name.
 *   shopify_sync.php:240-241  .sv-bulkbar .form-control-sm / .form-select-sm
 *                             { padding: 5px 14px; min-height: 34px } (0,2,0)
 *                             — the bulk bar's controls jump 34px to 42px.
 *   product_sync.php:761      select.fl-collection-select              (0,1,1)
 *
 * tax_settings.php:349 already uses !important and survives untouched — evidence
 * the codebase learned this lesson once already.
 *
 * The module sheets' own adornment rules (the select caret, the datatable search
 * magnifier, the per-page selector, edit_order's currency prefix) are safe by
 * construction: they load AFTER this file, so they win on source order as well
 * as specificity. That is strictly safer than the arrangement they had, where
 * they were out-specifying a sizing group in the same file — but any comment in
 * those sheets that explains the win as a specificity win is now stale and
 * should be rewritten to say "source order", or the next reader re-derives it
 * against a group that is no longer there.
 */
body:has(.fl-playful) .fl-playful .form-control,
body:has(.fl-playful) .fl-playful .form-select,
body:has(.fl-playful) .fl-playful .form-control:disabled,
body:has(.fl-playful) .fl-playful .form-control[readonly] {
    background-color: var(--pl-card);
    border: var(--pl-border);
    border-radius: 999px;
    box-shadow: var(--pl-shadow-sm);
    color: var(--pl-ink);
    font-family: var(--pl-body);
    font-size: 14px;
    font-weight: 600;
    min-height: 42px;
    padding: 9px 16px;
}
/* Neither orders nor customers declared these two arms at all, so disabled and
   readonly controls on those pages pick up the band fill and the lighter ink for
   the first time. */
body:has(.fl-playful) .fl-playful .form-control:disabled,
body:has(.fl-playful) .fl-playful .form-control[readonly] {
    background-color: var(--pl-band);
    box-shadow: var(--pl-shadow-xs);
    color: var(--pl-ink-2);
}

/* Multi-line content cannot be a capsule without the first and last lines
   colliding with the curve, so it takes the static rounded rect instead — the
   one place §4.3's interactive/static split yields, and it yields to legibility.
   A list box is multi-line for exactly the same reason a textarea is, and
   without this its first and last rows are clipped by the curve.
   RESOLVED: settings is min-height 84px / padding 12px 16px, orders and
   customers 88px / 11px 15px. Settings wins.
   NOT SHARED: orders and customers also match the BARE `textarea` element. This
   rule is class-qualified only — see the bare-element note under the caret rule
   below. */
body:has(.fl-playful) .fl-playful textarea.form-control,
body:has(.fl-playful) .fl-playful select[multiple].form-control,
body:has(.fl-playful) .fl-playful select[multiple].form-select,
body:has(.fl-playful) .fl-playful select[size]:not([size="1"]).form-control,
body:has(.fl-playful) .fl-playful select[size]:not([size="1"]).form-select {
    border-radius: var(--pl-radius);
    line-height: 1.5;
    min-height: 84px;
    padding: 12px 16px;
    resize: vertical;
}
/* A list box has no single closed value, so it has nothing to point a caret at. */
body:has(.fl-playful) .fl-playful select[multiple].form-select,
body:has(.fl-playful) .fl-playful select[size]:not([size="1"]).form-select { background-image: none; }

/* RESOLVED: `outline-offset` is 2px here and 3px on the generic `:focus-visible`
   above. That asymmetry is deliberate, not drift — a control already carries a
   3px stamped shadow, so the ring needs less air to clear it than a bare
   focusable element does. Orders and customers used 3px on both; settings wins,
   so focus rings on their controls tighten by 1px. Small, and it WILL show in
   the baseline diff. */
body:has(.fl-playful) .fl-playful .form-control:focus,
body:has(.fl-playful) .fl-playful .form-select:focus {
    background-color: var(--pl-card);
    border-color: var(--pl-ink);
    box-shadow: var(--pl-shadow-sm);
    color: var(--pl-ink);
    outline: 3px solid var(--pl-accent);
    outline-offset: 2px;
}
/* §6.2 puts placeholders at --pl-ink-2; all three sheets independently went one
   step lighter, because these forms use real candidate values as prompts and
   they must read as suggestions rather than as something already filled in.
   RESOLVED — reach: orders and customers used a BARE `::placeholder`. This is
   class-qualified, because a bare one would reach unclassed inputs on settings
   pages: views/shopify_sync.php builds `<input type="text" class="sv-input
   sv-sku" placeholder="—">` at runtime with no Bootstrap class on it. */
body:has(.fl-playful) .fl-playful .form-control::placeholder {
    color: var(--pl-ink-3);
    font-weight: 400;
    opacity: 1;
}

/*
 * The caret. Bootstrap draws it as a base64 grey chevron, which untouched is the
 * one piece of un-inked chrome on the page.
 *
 * THIS RULE MUST STAY BELOW THE `padding` SHORTHAND ABOVE. Its 38px right
 * reserve is the second half of the adornment coupling described there — emitted
 * before the shorthand, the shorthand resets it from inside this very file and
 * the caret gets drawn over the value.
 *
 * The caret is a background-IMAGE (style.css:2411), so writing the `background`
 * SHORTHAND anywhere near a select deletes it and the control silently loses its
 * chevron. Only `background-color` is ever set on a select in this file.
 *
 * RESOLVED — mechanism and values: orders repointed Bootstrap's
 * `--bs-form-select-bg-img` variable; settings writes `background-image`
 * directly. Settings wins, so the orders variable repoint is now dead and should
 * be deleted from that sheet. Stroke width 2.5 (customers drew 2), reserve 38px
 * (orders reserved 36px), caret pinned right 14px (orders 13px).
 *
 * NOT SHARED, and this is one of the two cascade fixes made shortly before the
 * migration: the qualified `select.form-control` / `select.form-select`
 * `appearance: none` rule stays in the ORDERS sheet. Settings pages ship
 * `<select class="form-control">` (views/get_paid.php:492) and `<select
 * class="form-control" multiple>` (views/product_sync.php:171) and settings never
 * sets `appearance: none` on either, so both keep the browser's native caret
 * today. A shared version would strip that native caret, draw its own, and put a
 * caret on a multi-select this file explicitly blanks two rules up. That is a
 * settings diff on at least two pages.
 */
body:has(.fl-playful) .fl-playful .form-select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2326201a' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    background-position: right 14px center;
    background-repeat: no-repeat;
    background-size: 15px;
    cursor: pointer;
    padding-right: 38px;
}

/* The file input's button half is a separate surface Bootstrap paints grey. The
   first line is itself a padding shorthand deliberately overriding the sizing
   group above — keep the order. Customers had already duplicated this capsule in
   its edit sheet at slightly different padding; settings' 6px 14px wins. */
body:has(.fl-playful) .fl-playful .form-control[type="file"] { padding: 6px 16px 6px 6px; }
body:has(.fl-playful) .fl-playful .form-control::file-selector-button {
    background: var(--pl-sun);
    border: var(--pl-border);
    border-radius: 999px;
    color: var(--pl-ink);
    font-family: var(--pl-display);
    font-size: 13px;
    font-weight: 700;
    margin-inline-end: 12px;
    padding: 6px 14px;
}
body:has(.fl-playful) .fl-playful .form-control:hover::file-selector-button { background: var(--pl-sun-hi); }

/* --- checkboxes, radios, switches --- */

/*
 * A bare `<input type="checkbox">` or `<input type="radio">` — one with no
 * Bootstrap class on it — is painted by the browser in its own blue, which is
 * the one colour on these pages that belongs to nothing. `accent-color` is the
 * whole lever available on a native control, it is the right one, and it costs
 * nothing on the ones fully restyled below.
 *
 * NOT SHARED: orders also sized these at 16px square. Promoting that would
 * resize every unclassed checkbox on the settings pages that have them.
 */
body:has(.fl-playful) .fl-playful input[type="checkbox"],
body:has(.fl-playful) .fl-playful input[type="radio"] { accent-color: var(--pl-accent); }

/*
 * `.form-check-input` is `appearance: none` painted with SVG data-URI marks
 * (style.css:2497). Only background-COLOR, border, radius and shadow are ever
 * set here; `background-image` is left to Bootstrap, or the tick and the switch
 * knob vanish. All three sheets found that trap independently.
 *
 * RESOLVED: settings sizes in px (20 x 20) and adds cursor and a 0.15em top
 * margin; orders and customers sized in em (1.2em, about 16.8px at 14px type).
 * Settings wins, so checkboxes on those seven pages grow to a flat 20px.
 *
 * NOT SHARED: orders' `.form-check-input[type=checkbox] { border-radius: 6px }`.
 * Settings never overrides Bootstrap's 0.25em, so promoting it would re-corner
 * every checkbox on 22 settings pages.
 *
 * VERIFY DURING MIGRATION: users_permissions.php:561's
 * `.furloop-settings-page .form-check-input:indeterminate` is (0,3,0) and would
 * now lose a tie to (0,3,1). It survives because the block below sets
 * background-color and border on `:checked` only, never on `:indeterminate` —
 * but that is a survival by luck, so check it rather than assume it.
 */
body:has(.fl-playful) .fl-playful .form-check-input {
    background-color: var(--pl-card);
    border: 2px solid var(--pl-ink);
    box-shadow: var(--pl-shadow-xs);
    cursor: pointer;
    height: 20px;
    margin-top: 0.15em;
    width: 20px;
}
body:has(.fl-playful) .fl-playful .form-check-input:checked {
    background-color: var(--pl-accent);
    border-color: var(--pl-ink);
}
body:has(.fl-playful) .fl-playful .form-check-input:focus {
    border-color: var(--pl-ink);
    box-shadow: var(--pl-shadow-xs);
    outline: 3px solid var(--pl-accent);
    outline-offset: 2px;
}
body:has(.fl-playful) .fl-playful .form-check-label { color: var(--pl-ink); cursor: pointer; }

/* The switch knob is a base64 circle in Bootstrap's own grey; both states need
   replacing or the toggle stays un-inked. The theme's unchecked knob is
   rgba(0,0,0,.25), which beside a 2px ink outline reads as smudged. The checked
   knob stays white against the accent.
   RESOLVED — a genuine structural disagreement, not a value. Orders deliberately
   refused to touch the geometry, on the grounds that width, the input's negative
   margin and the wrapper's padding-left are a single coupled measurement and
   moving one without the other two pushes the control out of its row. Settings
   moved all three together and explained why: Bootstrap reserves 2.5em for the
   knob, the ink switch is wider than 2.5em, so it overhangs — and the first line
   of an inline label then starts further right than every line under it, which
   reads as a typo rather than as indentation. Settings wins, so switches on the
   order and customer pages visibly resize (to 48 x 26px) and orders' two
   `--bs-form-switch-bg` repoints become dead code. Orders' coupling argument is
   still correct, which is exactly why the padding pair below travels with the
   width and must never be separated from it. */
body:has(.fl-playful) .fl-playful .form-switch .form-check-input {
    background-color: var(--pl-band);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2326201a'/%3E%3C/svg%3E");
    background-position: left 2px center;
    border-radius: 999px;
    height: 26px;
    width: 48px;
}
body:has(.fl-playful) .fl-playful .form-switch .form-check-input:checked {
    background-color: var(--pl-accent);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23ffffff'/%3E%3C/svg%3E");
    background-position: right 2px center;
}
/* The other two thirds of that coupled measurement. Widening the well is only
   right where the label is INSIDE the .form-check: several pages put the copy in
   a sibling element, and there the padding would open an empty gap in FRONT of
   the switch instead of behind it. The `:has()` gate is the whole reasoning and
   must travel with the rule. Safe against the padding trap: `padding-left` is a
   longhand on the wrapper, not on the control. */
body:has(.fl-playful) .fl-playful .form-switch:has(.form-check-label) { padding-left: 62px; }
body:has(.fl-playful) .fl-playful .form-switch:has(.form-check-label) .form-check-input { margin-left: -62px; }

/*
 * Helper text.
 * RESOLVED: settings is 13px, orders and customers 12.5px. Settings wins.
 *
 * SHIPPED WITHOUT !IMPORTANT, DELIBERATELY, AND THIS IS A MIGRATION DEPENDENCY.
 * Settings carries `color: var(--pl-ink-2) !important` here, and the !important
 * is real: it answers furloop-settings-pages.css:224, which sets
 * `color: var(--fl-text-muted) !important` on the same selector pair. !important
 * beats non-important at any specificity, so rising to (0,3,1) cannot win that
 * fight — only another !important can. But an !important in a SHARED layer
 * cannot be neutralised by a module loading second, which is the one mechanism
 * this whole split relies on, and it would export an unexplained !important to
 * two modules that have nothing to beat with it.
 *
 * So the escalation stays local: the settings remainder must keep its own
 * `.form-text, .furloop-helper-text { color: var(--pl-ink-2) !important }`. If
 * that line is dropped as "already covered here", every helper line on 22
 * settings pages reverts to the layout sheet's muted grey.
 */
body:has(.fl-playful) .fl-playful .form-text {
    color: var(--pl-ink-2);
    font-family: var(--pl-body);
    font-size: 13px;
    font-style: italic;
}

/* --- input groups --- */
/* Bootstrap furniture, including the BS4 prepend/append leftovers still in this
   codebase's markup. The 42px on `.input-group-text` is COUPLED to the control
   height above: a prefix that is not exactly as tall as the field it prefixes
   shows a step at the seam. Change one and you must change the other.
   This is also where a currency prefix lives, so it is inside the blast radius
   of the padding trap — audit prefix clearance alongside it. */
body:has(.fl-playful) .fl-playful .input-group { gap: 0; }
body:has(.fl-playful) .fl-playful .input-group-text {
    background: var(--pl-band);
    border: var(--pl-border);
    color: var(--pl-ink-2);
    font-family: var(--pl-mono);
    font-size: 12px;
    font-weight: 700;
    min-height: 42px;
}
body:has(.fl-playful) .fl-playful .input-group > :first-child,
body:has(.fl-playful) .fl-playful .input-group-prepend .input-group-text { border-radius: 999px 0 0 999px; }
body:has(.fl-playful) .fl-playful .input-group > :last-child,
body:has(.fl-playful) .fl-playful .input-group-append .btn,
body:has(.fl-playful) .fl-playful .input-group-append .input-group-text { border-radius: 0 999px 999px 0; }
/* Two stamped shadows that meet would double along the seam. */
body:has(.fl-playful) .fl-playful .input-group > :not(:last-child) { box-shadow: none; }

/* ============================================================= buttons ===== */
/*
 * §6.1's workhorse, and §8.4's first signature interaction — the most important
 * one in the system: the button is pushed flat against the paper. The shadow
 * does not shrink, it collapses to nothing.
 *
 * RESOLVED — THE headline shift of this whole migration. Settings is min-height
 * 42px / padding 9px 18px; orders and customers are 40px / 8px 16px. Settings
 * wins. Every button on the five order pages and the two customer pages grows
 * 2px taller and 2px wider on each side. Everything else already agreed
 * exactly: radius 999, 2px border, --pl-shadow-sm, display font at 14px/700,
 * line-height 1.2, gap 7px, inline-flex, centred both ways, no underline, and
 * the identical transition.
 *
 * NOT SHARED — three properties orders and customers set that settings does not,
 * and they must be restated in those module sheets or the modules lose them:
 *   text-transform: lowercase   §6.1 is explicit that Title Case breaks the
 *                               deadpan voice, and it is applied rather than
 *                               editing any string because most of those labels
 *                               come out of _lang() and belong to the reseller.
 *                               Promoting it would lowercase every button on 22
 *                               settings pages, so it cannot travel — but if the
 *                               module sheets do not restate it, every orders
 *                               and customers button silently reverts to Title
 *                               Case. That is a voice regression, not a
 *                               canonical-value adoption, and it is the easiest
 *                               thing in this migration to lose.
 *   letter-spacing: 0
 *   cursor: pointer
 *
 * In exchange, the two modules get `.btn[hidden]` below — a bug fix they never
 * had — plus the .btn-warning / .btn-info / .btn-outline-* families they never
 * declared, and .btn-lg's press.
 *
 * SOURCE ORDER INSIDE THIS SECTION IS LOAD-BEARING. Every variant selector below
 * scores the same (0,3,1), so when one element carries two of these classes the
 * later declaration wins. `.btn-light` and `.btn-link` are therefore declared
 * before `.btn-primary` and `.btn-secondary`, preserving the arrangement the
 * orders sheet arrived at after its only call to action — `<a class="btn
 * btn-light d-flex align-items-center btn-custom_a1">` — rendered as a tertiary
 * button. Adding a variant here means deciding where in that order it belongs.
 * (That particular CTA is now safe twice over: `.btn-custom_a1` lives in the
 * orders sheet, which loads after this file, so its sun fill wins on source
 * order for free.)
 */
body:has(.fl-playful) .fl-playful .btn {
    align-items: center;
    background: var(--pl-card);
    border: var(--pl-border);
    border-radius: 999px;
    box-shadow: var(--pl-shadow-sm);
    color: var(--pl-ink);
    display: inline-flex;
    font-family: var(--pl-display);
    font-size: 14px;
    font-weight: 700;
    gap: 7px;
    justify-content: center;
    line-height: 1.2;
    min-height: 42px;
    padding: 9px 18px;
    text-decoration: none;
    transition: transform 0.1s ease, box-shadow 0.1s ease, background-color 0.1s ease;
}
body:has(.fl-playful) .fl-playful .btn:active {
    box-shadow: 0 0 0 var(--pl-ink);
    transform: translate(3px, 3px);
}

/*
 * MUST TRAVEL WITH THE BASE RULE ABOVE. An author `display` beats the browser's
 * own `[hidden] { display: none }` whatever its specificity, so the
 * `display: inline-flex` above would make every `<button hidden>` visible. This
 * wins it back at (0,4,2) — above the rule it is correcting, and still below an
 * inline style, so jQuery's .show() keeps working.
 *
 * (Settings' original comment claimed 0,2,1 here. That number was true when the
 * rule lived at `.furloop-settings-page .btn[hidden]`; it is 0,4,2 now. The
 * claim it is making — above the base rule, below an inline style — is still
 * exactly right.)
 *
 * Orders and customers never had this rule, so a `<button hidden>` on those
 * pages is currently VISIBLE. Neither module's markup has one today, so the
 * probe should show no delta, but the latent bug is real and this closes it.
 */
body:has(.fl-playful) .fl-playful .btn[hidden] { display: none; }
body:has(.fl-playful) .fl-playful .btn:hover { color: var(--pl-ink); }

/*
 * RESOLVED — settings adds the `[aria-disabled="true"]` arm and `transform:
 * none`, and drops to --pl-shadow-xs where orders held --pl-shadow-sm. Settings
 * wins, so disabled buttons on order pages take a 2px shadow instead of 3px and
 * stop honouring any active transform.
 *
 * NOT SHARED — `cursor: wait`, and this is a genuine conflict rather than a
 * value choice. Settings and orders both set it, and both justify it with the
 * same sentence: "disabled here always means waiting on the server, never not
 * allowed". That claim is a statement about the settings domain and it is FALSE
 * on orders, where a bulk action is disabled because nothing is selected —
 * `cursor: wait` there tells the user to wait for something that will never
 * happen. So the generic treatment ships and the cursor stays in the settings
 * remainder with its §6.1 justification, for orders and customers to opt into
 * if they decide they want it.
 */
body:has(.fl-playful) .fl-playful .btn:disabled,
body:has(.fl-playful) .fl-playful .btn.disabled,
body:has(.fl-playful) .fl-playful .btn[aria-disabled="true"] {
    box-shadow: var(--pl-shadow-xs);
    opacity: 0.6;
    transform: none;
}

/*
 * §6.1's variant table, mapped onto the pastels. Hover is a fill change only —
 * the lift belongs to cards, not to buttons, or the press has something to
 * fight.
 *
 * A link-button is marker on the page, not a sticker.
 * RESOLVED: orders and customers grouped `.btn-link` with `.btn-light` and gave
 * it the full capsule — 2px outline, 3px shadow, rose on hover. Settings gives
 * it no outline, no shadow and an accent underline. Settings wins, so a
 * `.btn-link` on an order or customer page loses its outline and shadow
 * entirely and becomes an underlined text link. Visible, and reportable.
 */
body:has(.fl-playful) .fl-playful .btn-link {
    background: transparent;
    border-color: transparent;
    box-shadow: none;
    color: var(--pl-ink);
    text-decoration: underline;
    text-decoration-color: var(--pl-accent);
    text-decoration-thickness: 2px;
    text-underline-offset: 3px;
}
body:has(.fl-playful) .fl-playful .btn-link:hover { background: transparent; color: var(--pl-accent); }
body:has(.fl-playful) .fl-playful .btn-link:active { box-shadow: none; transform: none; }

/* `.btn-save-eo` is a legacy PRODUCT-WIDE primary alias — edit_location.php,
   controllers/repair/add_order.php, product/edit_product.php, shipping_api.php —
   not settings vocabulary, so it belongs in the shared variant table. It is
   inert on any page that does not carry `fl-playful`.
   HAZARD for the settings migration: users_permissions.php:696's
   `.up-upgrade .btn { background: var(--pl-card) }` (0,2,0) sits on a
   `btn btn-primary` at :1208 and today wins on source order. It will now lose to
   this rule, and its own comment says why that matters — "the primary button is
   normally the sun, which would disappear here", on a yellow plate. It must be
   restated at (0,3,1). Same shape at views/printers.php:41, whose
   `.btn-add-printer { background: var(--pl-sun) }` will lose to the base `.btn`
   above and turn the page's only CTA white; that page's comment at :39-40 even
   documents the assumption being broken. */
body:has(.fl-playful) .fl-playful .btn-primary,
body:has(.fl-playful) .fl-playful .btn-save-eo {
    background: var(--pl-sun);
    border-color: var(--pl-ink);
    color: var(--pl-ink);
}
/* RESOLVED: settings additionally pins box-shadow and `transform: none` on the
   hover state; orders did not. Settings wins. */
body:has(.fl-playful) .fl-playful .btn-primary:hover,
body:has(.fl-playful) .fl-playful .btn-primary:focus,
body:has(.fl-playful) .fl-playful .btn-save-eo:hover {
    background: var(--pl-sun-hi);
    border-color: var(--pl-ink);
    box-shadow: var(--pl-shadow-sm);
    color: var(--pl-ink);
    transform: none;
}

/* RESOLVED: `.btn-light`. Orders and customers grouped it with tertiary — card
   white at rest, ROSE on hover. Settings groups it with secondary — card white
   at rest, SKY on hover. Settings wins, so `.btn-light` on an order or customer
   page stops hovering pink and hovers blue. */
body:has(.fl-playful) .fl-playful .btn-secondary,
body:has(.fl-playful) .fl-playful .btn-outline-secondary,
body:has(.fl-playful) .fl-playful .btn-light { background: var(--pl-card); color: var(--pl-ink); }
body:has(.fl-playful) .fl-playful .btn-secondary:hover,
body:has(.fl-playful) .fl-playful .btn-outline-secondary:hover,
body:has(.fl-playful) .fl-playful .btn-light:hover { background: var(--pl-sky); color: var(--pl-ink); }

body:has(.fl-playful) .fl-playful .btn-success,
body:has(.fl-playful) .fl-playful .btn-outline-success,
body:has(.fl-playful) .fl-playful .btn-info,
body:has(.fl-playful) .fl-playful .btn-outline-info {
    background: var(--pl-sky);
    border-color: var(--pl-ink);
    color: var(--pl-ink);
}
body:has(.fl-playful) .fl-playful .btn-success:hover,
body:has(.fl-playful) .fl-playful .btn-info:hover { background: var(--pl-sky-hi); color: var(--pl-ink); }

body:has(.fl-playful) .fl-playful .btn-warning,
body:has(.fl-playful) .fl-playful .btn-outline-warning {
    background: var(--pl-watch-paper);
    border-color: var(--pl-ink);
    color: var(--pl-ink);
}
body:has(.fl-playful) .fl-playful .btn-warning:hover { background: var(--pl-sun); }

/* Destructive sits on the pink at rest and only reaches for the accent under the
   cursor. §2.3 keeps the one loud colour small and momentary.
   RESOLVED, and this one is also the accessible answer: orders and customers set
   `color: var(--pl-card)` on the hover state — white on #ff5c2b, which measures
   3.07:1 and fails AA for label-sized type. Settings keeps --pl-ink, which
   measures 5.24:1 on the same orange. Settings wins, so danger-button hover text
   on those pages goes from white to warm ink. Report it as a fix, not just a
   colour change; two more instances of the same white-on-accent defect live in
   the customers sheet and should be corrected there the same way. */
body:has(.fl-playful) .fl-playful .btn-danger,
body:has(.fl-playful) .fl-playful .btn-outline-danger {
    background: var(--pl-rose);
    border-color: var(--pl-ink);
    color: var(--pl-ink);
}
body:has(.fl-playful) .fl-playful .btn-danger:hover,
body:has(.fl-playful) .fl-playful .btn-outline-danger:hover {
    background: var(--pl-accent);
    border-color: var(--pl-ink);
    color: var(--pl-ink);
}

/* RESOLVED: orders grouped `.btn-outline-primary` with secondary and hovered it
   SKY. Settings gives it its own group and hovers it SUN. Settings wins, so
   outline-primary buttons on order pages hover yellow instead of blue. */
body:has(.fl-playful) .fl-playful .btn-outline-primary {
    background: var(--pl-card);
    border-color: var(--pl-ink);
    color: var(--pl-ink);
}
body:has(.fl-playful) .fl-playful .btn-outline-primary:hover,
body:has(.fl-playful) .fl-playful .btn-outline-primary:focus {
    background: var(--pl-sun);
    border-color: var(--pl-ink);
    color: var(--pl-ink);
}

/* §4.1 — shadow offset scales with element size, so the small button takes the
   micro shadow and a 2px press rather than 3px, and the large one a 4px press.
   RESOLVED, small: settings 5px 13px / 12.5px against orders' and customers'
   4px 13px / 13px. RESOLVED, large: settings 52px / 12px 26px against orders'
   50px / 12px 24px, and settings' `.btn-lg:active` is a rule orders never had.
   Settings wins throughout. Both are padding shorthands — audit any page rule
   that adorns a `.btn-sm`. */
body:has(.fl-playful) .fl-playful .btn-sm {
    box-shadow: var(--pl-shadow-xs);
    font-size: 12.5px;
    min-height: 32px;
    padding: 5px 13px;
}
body:has(.fl-playful) .fl-playful .btn-sm:active { box-shadow: 0 0 0 var(--pl-ink); transform: translate(2px, 2px); }

body:has(.fl-playful) .fl-playful .btn-lg {
    box-shadow: var(--pl-shadow);
    font-size: 16px;
    min-height: 52px;
    padding: 12px 26px;
}
body:has(.fl-playful) .fl-playful .btn-lg:active { box-shadow: 0 0 0 var(--pl-ink); transform: translate(4px, 4px); }

/* Bootstrap's × is a base64 SVG drawn `fill='%23000000'` at 0.5 opacity
   (style.css:5581) — pure black inside a warm-ink surface, which §12 forbids
   outright. Redrawn in ink at full strength. Must land before or with the modal
   block, which reuses it.
   NOT SHARED: orders turns the close control into a full 30px capsule sticker.
   Settings' `.btn-close` also reaches DISMISSIBLE ALERTS, not just modals, so
   promoting the capsule would put an outlined, shadowed, rose-hovering button on
   every dismissible alert across 22 settings pages. It stays in the orders
   sheet, qualified to `.modal-header`. */
body:has(.fl-playful) .fl-playful .btn-close {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%2326201a' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M3 3l10 10M13 3L3 13'/%3E%3C/svg%3E");
    opacity: 1;
}
body:has(.fl-playful) .fl-playful .btn-close:hover { opacity: 0.6; }

/* ============================================================== alerts ===== */
/*
 * §2.3 assigns sky to "confirmed" and orange to "something happened". Success,
 * warning and danger map onto sky, butter and pink rather than inventing a
 * fourth signal; the loud orange stays reserved for active state.
 *
 * Bootstrap applies the border through `border: var(--bs-alert-border)`
 * (style.css:5080) — a SHORTHAND re-resolved from a variable — so setting only
 * `border-color` moves nothing. The full shorthand is written instead. All three
 * sheets found this independently.
 *
 * RESOLVED, three ways:
 *   shadow    orders and customers used --pl-shadow (4px); settings uses
 *             --pl-shadow-xs (2px). Settings wins, so alerts on those pages
 *             flatten noticeably.
 *   padding   13px 18px becomes 14px 18px.
 *   mapping   orders gave the bare `.alert` a butter fill with olive text and
 *             mapped `.alert-info` to butter/olive; settings leaves the bare
 *             alert unfilled with ink text and maps info/secondary/light to the
 *             band. Settings wins, which is visible on every order page that
 *             shows a flash message.
 *
 * NOT SHARED: orders and customers rotate the alert (`transform:
 * rotate(0.45deg)`) and customers makes it a flex row with a 10px gap. Settings
 * alerts are neither tilted nor flex today, so promoting either would break the
 * zero-diff invariant. Both stay module-side.
 */
body:has(.fl-playful) .fl-playful .alert {
    border: var(--pl-border);
    border-radius: var(--pl-radius);
    box-shadow: var(--pl-shadow-xs);
    color: var(--pl-ink);
    font-size: 14px;
    padding: 14px 18px;
}
body:has(.fl-playful) .fl-playful .alert-success { background: var(--pl-sky); border-color: var(--pl-ink); color: var(--pl-ink); }
body:has(.fl-playful) .fl-playful .alert-info,
body:has(.fl-playful) .fl-playful .alert-secondary,
body:has(.fl-playful) .fl-playful .alert-light { background: var(--pl-band); border-color: var(--pl-ink); color: var(--pl-ink); }
body:has(.fl-playful) .fl-playful .alert-danger { background: var(--pl-rose); border-color: var(--pl-ink); color: var(--pl-ink); }

/* §6.12 — small text on the butter card takes the olive, not the grey ink. Bold
   runs inside it go back to full ink so the emphasis still reads. */
body:has(.fl-playful) .fl-playful .alert-warning { background: var(--pl-watch-paper); border-color: var(--pl-ink); color: var(--pl-olive); }
body:has(.fl-playful) .fl-playful .alert-warning strong,
body:has(.fl-playful) .fl-playful .alert-warning b { color: var(--pl-ink); }

body:has(.fl-playful) .fl-playful .alert a { color: var(--pl-ink); }
body:has(.fl-playful) .fl-playful .alert .btn-close { background-color: transparent; }

/* ============================================================== tables ===== */
/*
 * Ledger vocabulary. Bootstrap furniture throughout, and the place where the
 * theme fights back hardest.
 *
 * TWO OMISSIONS THAT ARE DELIBERATE AND MUST STAY OMISSIONS:
 *
 *   1. `.table thead th` sets NO padding. The theme forces `padding: 1rem 1rem
 *      !important` at style.css:30159 — the only !important in its table block —
 *      and settings has always let it stand. Setting padding here would retighten
 *      every settings table header, which is a visible settings diff. Orders and
 *      customers answer that !important in kind from their own sheets, with
 *      `padding: 11px 14px !important`, and that answer must STAY in those
 *      sheets: hoisting it here would be the same diff from the other direction.
 *      A mono kicker row genuinely is tighter than 16px, but only on the two
 *      modules that asked for it.
 *
 *   2. The row-hover mechanism. Bootstrap paints row state with an
 *      `inset 0 0 0 9999px` shadow on every cell (style.css:1991), so a
 *      background on the <tr> is painted over, and any box-shadow written on a
 *      <td> destroys the mechanism outright. Settings targets the CELLS directly
 *      to sidestep it; orders and customers drive it through
 *      `--bs-table-hover-bg`. Both are valid answers to the same defect, and the
 *      datatable path needs the variable route, so the modules keep theirs.
 *
 * RESOLVED, four ways, all to settings:
 *   thead font-size   10.5px becomes 11px.
 *   thead colour      --pl-ink becomes --pl-ink-2.
 *   tbody colour      --pl-ink-2 becomes --pl-ink — the body cells go one step
 *                     DARKER while the table element itself stays ink-2.
 *   row hairline      rgba(38,32,26,.18) becomes the solid cream --pl-empty.
 * Plus `.table tbody tr:last-child td { border-bottom: 0 }`, which orders and
 * customers never had, so their tables lose their final hairline.
 *
 * `.table-responsive` gains a 2px top border on the order and customer pages,
 * which is settings' value. Its 6px bottom padding does NOT travel: that answers
 * two nested `overflow-x: auto` boxes slicing the last row's 4px shadow, and
 * `.table-responsive` appears on four settings pages that set no padding today.
 */
body:has(.fl-playful) .fl-playful .table-responsive { border-top: var(--pl-border); }
body:has(.fl-playful) .fl-playful .table {
    color: var(--pl-ink-2);
    font-size: 13.5px;
    --bs-table-bg: transparent;
    --bs-table-color: var(--pl-ink);
}
body:has(.fl-playful) .fl-playful .table thead th {
    background: var(--pl-band);
    border-bottom: var(--pl-border);
    color: var(--pl-ink-2);
    font-family: var(--pl-mono);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}
body:has(.fl-playful) .fl-playful .table tbody td {
    border-bottom: 1.5px solid var(--pl-empty);
    color: var(--pl-ink);
}
body:has(.fl-playful) .fl-playful .table tbody tr:hover > * { background: var(--pl-row-hover); }
body:has(.fl-playful) .fl-playful .table tbody tr:last-child td { border-bottom: 0; }

/* ------------------------------------------------ simple-datatables chrome --
 *
 * THE EVIDENCE, because this was the closest call in the migration.
 *
 * Datatables really is generic by reach. includes/footer.php:333-342 runs ONE
 * init for the whole dashboard — any page containing `#pc-dt-simple`, minus two
 * hardcoded filename exclusions — and that markup appears in all three modules:
 * views/location.php (a SETTINGS page), the three orders list pages and
 * bulk_sell, and views/customers.php. So the chrome qualifies.
 *
 * But the three modules' coverage is NOT equivalent, and that decides how much
 * of it moves. Settings declares five loose rules, deliberately lighter and
 * structurally different: band-tone bars, --pl-hair borders, no capsule shadows,
 * no flex, 5px 12px padding. Orders and customers declare roughly sixty rules
 * each — a flex top bar, a 17rem capsule search box with a 3px offset shadow, a
 * repositioned Font Awesome magnifier, a 138px !important selector, a repainted
 * footer bar and a full button-based pager — every one of them written against
 * includes/footer.php and style.css BY LINE NUMBER, and against two runtime
 * timing facts (footer.php's <style> is emitted after the view, and its JS
 * relocates the per-page dropdown and stamps inline styles ~100ms after init).
 *
 * Promoting that would land all of it on location.php, which has none of it
 * today. That is a large, unambiguous settings-page computed-style diff — a bug
 * by the invariant. Promoting it in the other direction is no better: settings'
 * own `.datatable-pagination a` rules are DEAD CODE, because simple-datatables
 * emits `<button>`, not `<a>`, so style.css:30704's matching block is dead too
 * and location.php's pager is currently painted by footer.php's own purple
 * #6C5CE7 inline <style>. "Canonical settings wins" would therefore ship a
 * functional regression to the two modules that had actually solved it.
 *
 * SO: only the subset settings itself declares moves, at settings' values, and
 * strictly !important-free. Everything else — the table, the empty-state cell,
 * the wrapper, the search magnifier, the entries label, the info line and the
 * whole pager — stays in the orders and customers sheets, which load second and
 * therefore override every property below that they also declare. Those sheets'
 * nine !important declarations stay with them for the same reason: an
 * !important here could not be answered by a module loading second.
 *
 * The dead `.datatable-pagination a` rules are kept, byte-for-byte, because
 * removing them is only safe if nothing anywhere re-enables anchor pagers, and
 * because keeping them is what makes this block lossless for settings. They are
 * inert on all three modules.
 *
 * PREDICTED DELTA, and the migration agents should decide about it explicitly:
 * orders and customers style `.datatable-top` as a transparent flex bar (so the
 * search capsule's shadow can escape `overflow: hidden`) and set no background,
 * colour or font on it. The band fill below is a property they do not declare,
 * so it WILL land on them. If a cream top bar is unwanted there, the fix is one
 * additive line — `background: transparent` — in each module sheet, which wins
 * on source order. Their `.datatable-bottom` already carries
 * `background-color: var(--pl-paper) !important` and is unaffected.
 *
 * This block is the number-one candidate for a second pass, once a location.php
 * baseline exists to check the full chrome against.
 */
body:has(.fl-playful) .fl-playful .datatable-top,
body:has(.fl-playful) .fl-playful .datatable-bottom {
    background: var(--pl-band);
    border-color: var(--pl-ink);
    color: var(--pl-ink-2);
    font-family: var(--pl-mono);
    font-size: 12px;
}
body:has(.fl-playful) .fl-playful .datatable-input,
body:has(.fl-playful) .fl-playful .datatable-selector {
    background: var(--pl-card);
    border: var(--pl-hair);
    border-radius: 999px;
    color: var(--pl-ink);
    font-family: var(--pl-body);
    font-size: 13px;
    padding: 5px 12px;
}
body:has(.fl-playful) .fl-playful .datatable-pagination a {
    border-radius: 999px;
    color: var(--pl-ink);
    font-family: var(--pl-display);
    font-weight: 700;
    text-decoration: none;
}
body:has(.fl-playful) .fl-playful .datatable-pagination .datatable-active a,
body:has(.fl-playful) .fl-playful .datatable-pagination a:hover {
    background: var(--pl-sun);
    border: var(--pl-hair);
    color: var(--pl-ink);
}

/* =========================================================== pagination ==== */
/* Bootstrap's own pager, which is a different thing from the datatable's. */
body:has(.fl-playful) .fl-playful .pagination { gap: 6px; }
body:has(.fl-playful) .fl-playful .page-link {
    background: var(--pl-card);
    border: var(--pl-hair);
    border-radius: 999px;
    color: var(--pl-ink);
    font-family: var(--pl-display);
    font-weight: 700;
}
body:has(.fl-playful) .fl-playful .page-item.active .page-link { background: var(--pl-sun); border-color: var(--pl-ink); color: var(--pl-ink); }

/* ============================================================== badges ===== */
/*
 * §6.11. A badge is a small sticker: capsule, hairline outline, micro shadow,
 * mono uppercase. Text is always ink — the pastels are chosen light enough to
 * keep it legible, which is why none of them carries white type. §4.1 forbids
 * blur, spread and tint on a shadow, so the ink shadow is the whole treatment.
 *
 * RESOLVED: settings is 11px with 3px 10px 4px of padding; orders and customers
 * are 10.5px with 3px 11px. Settings wins.
 *
 * NOT SHARED: customers also sets a default `background-color: var(--pl-card)`.
 * A settings `.badge` carrying no `.bg-*` class would flip from the Bootstrap
 * default to white, which is a settings diff. It stays in the customers sheet.
 * Also not shared: the four !important declarations orders attaches to
 * `.label` / `.label-inline`. Those answer an inline `style` attribute on the
 * orders list pill whose background is the RESELLER'S OWN configured colour —
 * the shape moves and the fill stays, because recolouring it would be overruling
 * a setting. An inline attribute is the one thing specificity cannot outrank, so
 * those four must live where the markup does. No settings page carries
 * `.label-inline`.
 */
body:has(.fl-playful) .fl-playful .badge {
    border: var(--pl-hair);
    border-radius: 999px;
    box-shadow: var(--pl-shadow-xs);
    color: var(--pl-ink);
    font-family: var(--pl-mono);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    padding: 3px 10px 4px;
    text-transform: uppercase;
}

/*
 * THE ONE !IMPORTANT GROUP IN THIS FILE, and it is non-negotiable. It answers
 * TWO unconditional things at once, neither of which specificity can reach:
 * Bootstrap's own `!important` `.bg-*` colour utilities, and the Able Pro
 * theme's `.bg-light-success` (style.css:17590) and `.badge.bg-light-success`
 * (style.css:22844). Rising to (0,3,1) does not remove the need, because
 * !important beats specificity.
 *
 * The three bare `.bg-light-*` selectors deliberately paint non-badge elements
 * too. That breadth is intentional and must be preserved — the theme applies
 * those classes to plates and pills that are not badges, and half-painting them
 * is worse than not painting them.
 *
 * Orders and customers never declared this family, so their pages gain coherent
 * badge colours where they currently fall through to the theme.
 *
 * RE-AUDIT NOTE FOR THE ORDERS MIGRATION: edit_order.php leaves `.pc-container`
 * and `.pc-content` unclosed, so on that page alone includes/footer.php's site
 * footer and theme-customiser offcanvas nest INSIDE the marker. The orders sheet
 * carries a shell carve-out for exactly that, scoring (1,2,1) — an id beats any
 * number of classes, so it still outranks everything else here. It does NOT beat
 * an !important. If any `bg-light-*` class appears in that offcanvas markup, it
 * will now be repainted on edit_order.php only. Grep the offcanvas before
 * shipping.
 */
body:has(.fl-playful) .fl-playful .badge.bg-success,
body:has(.fl-playful) .fl-playful .badge.bg-light-success,
body:has(.fl-playful) .fl-playful .bg-light-success { background: var(--pl-sky) !important; color: var(--pl-ink) !important; }
body:has(.fl-playful) .fl-playful .badge.bg-danger,
body:has(.fl-playful) .fl-playful .badge.bg-light-danger,
body:has(.fl-playful) .fl-playful .bg-light-danger { background: var(--pl-rose) !important; color: var(--pl-ink) !important; }
body:has(.fl-playful) .fl-playful .badge.bg-warning,
body:has(.fl-playful) .fl-playful .badge.bg-light-warning,
body:has(.fl-playful) .fl-playful .bg-light-warning { background: var(--pl-watch-paper) !important; color: var(--pl-olive) !important; }
body:has(.fl-playful) .fl-playful .badge.bg-primary,
body:has(.fl-playful) .fl-playful .badge.bg-light-primary,
body:has(.fl-playful) .fl-playful .bg-light-primary { background: var(--pl-sun) !important; color: var(--pl-ink) !important; }
body:has(.fl-playful) .fl-playful .badge.bg-info,
body:has(.fl-playful) .fl-playful .badge.bg-light-info { background: var(--pl-sky) !important; color: var(--pl-ink) !important; }
body:has(.fl-playful) .fl-playful .badge.bg-secondary,
body:has(.fl-playful) .fl-playful .badge.bg-light { background: var(--pl-band) !important; color: var(--pl-ink) !important; }
body:has(.fl-playful) .fl-playful .badge.bg-dark { background: var(--pl-ink) !important; color: var(--pl-paper) !important; }

/* ================================================================ tabs ===== */

body:has(.fl-playful) .fl-playful .nav-tabs { border-bottom: var(--pl-border); gap: 6px; }
body:has(.fl-playful) .fl-playful .nav-tabs .nav-link {
    background: transparent;
    border: 0;
    border-bottom: 3px solid transparent;
    color: var(--pl-ink-2);
    font-family: var(--pl-display);
    font-size: 14px;
    font-weight: 700;
    margin-bottom: -2px;
    padding: 9px 12px;
}
body:has(.fl-playful) .fl-playful .nav-tabs .nav-link:hover { color: var(--pl-ink); }
body:has(.fl-playful) .fl-playful .nav-tabs .nav-link.active {
    background: transparent;
    border-bottom-color: var(--pl-accent);
    color: var(--pl-ink);
}

body:has(.fl-playful) .fl-playful .nav-pills .nav-link {
    border: var(--pl-border);
    border-radius: 999px;
    box-shadow: var(--pl-shadow-xs);
    color: var(--pl-ink);
    font-family: var(--pl-display);
    font-weight: 700;
}
/* A DOCUMENTED AA DEPARTURE, and the kind of thing that gets "tidied" back.
   Ink, not white. §6.1's variant table pairs the accent fill with card-coloured
   text, but white on #ff5c2b measures 3.07:1 and fails AA for label-sized type;
   the ink measures 5.24:1 on the same orange. §10 is explicit that the playful
   surface does not cut corners underneath, so the ratio wins over the table.
   This is the same measurement that decides `.btn-danger:hover` above. */
body:has(.fl-playful) .fl-playful .nav-pills .nav-link.active { background: var(--pl-accent); color: var(--pl-ink); }

/* =========================================================== accordion ===== */

body:has(.fl-playful) .fl-playful .accordion { --bs-accordion-border-color: var(--pl-ink); }
body:has(.fl-playful) .fl-playful .accordion-item {
    background: var(--pl-card);
    border: var(--pl-border);
    border-radius: var(--pl-radius);
    box-shadow: var(--pl-shadow-xs);
    margin-bottom: 12px;
    overflow: hidden;
}
body:has(.fl-playful) .fl-playful .accordion-button {
    background: var(--pl-card);
    box-shadow: none;
    color: var(--pl-ink);
    font-family: var(--pl-display);
    font-size: 15px;
    font-weight: 700;
}
body:has(.fl-playful) .fl-playful .accordion-button:not(.collapsed) {
    background: var(--pl-sun);
    border-bottom: var(--pl-border);
    box-shadow: none;
    color: var(--pl-ink);
}
body:has(.fl-playful) .fl-playful .accordion-button::after {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2326201a' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}
body:has(.fl-playful) .fl-playful .accordion-body { background: var(--pl-card); }

/* =========================================================== dropdowns ===== */
/*
 * Anchored to `.fl-playful` rather than written against `.dropdown-menu`, and
 * that anchor is the whole point: the dashboard top bar's account, notification
 * and language menus live in `.pc-header`, a SIBLING of `.pc-container`. An
 * unanchored rule would re-skin them on the opted-in pages and leave them alone
 * on every other, which is the worst of both. This is the clearest single
 * illustration of why the descendant form is mandatory throughout this file.
 *
 * RESOLVED: orders made `.dropdown-item` a capsule — 999px radius, 7px 13px,
 * 13.5px, weight 600. Settings uses a 10px rounded rect at 8px 12px / 14px.
 * Settings wins, so dropdown items on order pages stop being capsules.
 * RESOLVED: the divider was 1.5px ink at 0.2 opacity in orders; settings uses a
 * full-opacity --pl-empty cream. Settings wins.
 */
body:has(.fl-playful) .fl-playful .dropdown-menu {
    background: var(--pl-card);
    border: var(--pl-border);
    border-radius: var(--pl-radius);
    box-shadow: var(--pl-shadow);
    padding: 6px;
}
body:has(.fl-playful) .fl-playful .dropdown-item {
    border-radius: 10px;
    color: var(--pl-ink);
    font-size: 14px;
    padding: 8px 12px;
}
body:has(.fl-playful) .fl-playful .dropdown-item:hover,
body:has(.fl-playful) .fl-playful .dropdown-item:focus { background: var(--pl-sun); color: var(--pl-ink); }
body:has(.fl-playful) .fl-playful .dropdown-divider { border-top: 1.5px solid var(--pl-empty); }

/* ==================================================== progress, spinner ==== */

body:has(.fl-playful) .fl-playful .progress {
    background: var(--pl-empty);
    border: var(--pl-hair);
    border-radius: 999px;
    box-shadow: var(--pl-shadow-xs);
    height: 16px;
    overflow: hidden;
}
body:has(.fl-playful) .fl-playful .progress-bar {
    background-color: var(--pl-accent);
    color: var(--pl-ink);
    font-family: var(--pl-mono);
    font-size: 10px;
    font-weight: 700;
}
body:has(.fl-playful) .fl-playful .spinner-border { border-right-color: transparent; color: var(--pl-accent); }

/* ================================================================ code ===== */
/*
 * Inline code, shipped WITHOUT the !important settings carries. That escalation
 * answers furloop-settings-pages.css:483 and :498, which set an SFMono stack
 * with !important on `.api-example code` and `.token-display` — a sheet only
 * settings loads. Same reasoning as `.form-text` above: the escalation stays
 * with the module that has something to beat, and the settings remainder must
 * keep its own copy or the API-token page reverts to SFMono.
 */
body:has(.fl-playful) .fl-playful code {
    color: var(--pl-ink);
    font-family: var(--pl-mono);
    font-size: 12.5px;
}

/* ============================================================== modals ===== */
/*
 * SPECIFICITY EXCEPTION, the second of two. Bootstrap can move a modal to the
 * end of <body>, outside the container, so the `.fl-playful X` descendant form
 * cannot reach modal content. `body:has(.fl-playful) .fl-playful-modal X` is
 * still exactly (0,3,1) and still matches after the move.
 *
 * `.fl-playful-modal` is an OPT-IN class that already exists and already has two
 * users — email_templates.php:333 and views/printers.php:353. Note the name
 * predates the `fl-playful` container class and is deliberately NOT the same
 * token: CSS class matching is exact, so `body:has(.fl-playful)` never matches
 * `.fl-playful-modal`, and the two do not collide.
 *
 * This narrows the selector from settings' bare `.fl-playful-modal` (0,2,0) to
 * pages that carry `fl-playful`. Both current users are settings pages that will
 * carry it, so there should be no diff — verify rather than assume.
 *
 * NO `.fl-playful .modal-content` ARM, deliberately. Adding one would reach every
 * settings modal that lives inside the wrapper and does NOT carry
 * `.fl-playful-modal` — several pages have those — which is a settings diff.
 * Orders' modals are reached from inside `.fl-ord-page` and keep their own rules
 * in the orders sheet; reconciling the two entry points needs a markup class the
 * migration contract does not authorise.
 *
 * MIGRATION TRAP, and it threatens the invariant from the other direction:
 * views/printers.php:355 is `<div class="modal-content furloop-settings-page">`.
 * The settings wrapper class is on the modal content ITSELF, precisely because
 * the modal escapes `.pc-container`. Today every `.furloop-settings-page X` rule
 * reaches inside it. After the split the generic half of those rules lives here
 * behind `.fl-playful`, which that element does not carry — so the entire dialog
 * silently loses the shared component layer: buttons, inputs, labels, badges,
 * the lot. It must gain `fl-playful` alongside, which is a legal additive edit.
 * It will not show up in a probe unless the probe opened the modal.
 *
 * RESOLVED: modal title 22px becomes 19px. Orders' modal-header radius and
 * padding, `.modal-body` padding and the `.modal-footer` treatment stay in the
 * orders sheet — promoting them would move email_templates.php and printers.php.
 */
body:has(.fl-playful) .fl-playful-modal .modal-content {
    background: var(--pl-card);
    border: var(--pl-border);
    border-radius: var(--pl-radius);
    box-shadow: var(--pl-shadow-hero);
    color: var(--pl-ink);
    font-family: var(--pl-body);
}
body:has(.fl-playful) .fl-playful-modal .modal-header {
    background: var(--pl-band);
    border-bottom: var(--pl-border);
}
body:has(.fl-playful) .fl-playful-modal .modal-title { font-family: var(--pl-display); font-size: 19px; font-weight: 800; }
body:has(.fl-playful) .fl-playful-modal .modal-footer { background: var(--pl-band); border-top: var(--pl-border); }
body:has(.fl-playful) .fl-playful-modal .btn-close {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%2326201a' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M3 3l10 10M13 3L3 13'/%3E%3C/svg%3E");
    opacity: 1;
}

/* ========================================================== responsive ===== */
/*
 * §9's single breakpoint.
 *
 * RESOLVED, and this is a media-query MERGE rather than a value choice, so it
 * deserves its own line in the delta report: settings breaks at 767px, orders
 * and customers at 640px. Settings wins, so the generic rule below now fires
 * 127px earlier on the order and customer pages than their own mobile blocks do.
 * Those blocks stay at 640px and the two will straddle. Either restate them at
 * 767px in the module sheets or document the straddle there — but do it
 * deliberately, because a component that changes at one width inside a layout
 * that changes at another is the kind of thing that only shows up on a tablet.
 *
 * A 3px stamped shadow is too heavy on a phone. The de-tilt reasoning that sits
 * beside this in the settings sheet — "a tilt is charming beside a neighbour and
 * looks like a mistake on a single full-width column" — is generic and worth
 * repeating here, because orders and customers will each need their own de-tilt
 * rules for their own card classes, and get_paid.php has already hand-copied one
 * for its payment cards. That is the pattern wanting a shared home it cannot
 * have without a shared markup class.
 */
@media (max-width: 767px) {
    body:has(.fl-playful) .fl-playful .btn { box-shadow: var(--pl-shadow-xs); }
}

/*
 * §8.5 — kill the transitions, keep the static rotations. The tilted-sticker look
 * is identity, not motion, so it survives: a user who has asked for less movement
 * has not asked for a different design. All four sheets state this policy in the
 * same words.
 *
 * ASYMMETRY WORTH KNOWING: on settings pages this is belt-and-braces, because
 * furloop-settings-pages.css:979-985 already nukes `transition-duration` with
 * !important across `.furloop-settings-page *`. No such blanket exists for
 * orders or customers, so here the rule is genuinely load-bearing for them.
 */
@media (prefers-reduced-motion: reduce) {
    body:has(.fl-playful) .fl-playful .btn,
    body:has(.fl-playful) .fl-playful .form-control,
    body:has(.fl-playful) .fl-playful .form-check-input { transition: none; }
}
