/* ============================================================
   components/tabs.css — tabbed panels (P20.2)

   Markup:
     <div class="dd-tabs" data-dd-tabs>
       <div class="dd-tabs__nav" role="tablist">
         <button class="dd-tabs__tab is-active" role="tab"
                 data-dd-tab="a" aria-selected="true">label.</button>
         …
       </div>
       <div class="dd-tabs__panel is-active" role="tabpanel" data-dd-tabpanel="a">…</div>
       <div class="dd-tabs__panel" role="tabpanel" data-dd-tabpanel="b" hidden>…</div>
     </div>

   Inactive panels keep the `hidden` attribute — they stay in the DOM so any
   HTMX `hx-swap-oob` targeting an element *inside* a panel still lands. Driven
   by the delegated handler in js/components.js (window.ddTabs).

   Two tabs may open one panel: `data-dd-tab-panel="a"` on a tab opens panel
   `a` instead of the panel named for the tab's own key, and the container
   fires `dd:tab` ({key, tab}) on every change, for whatever reads that panel
   differently per tab.

   `.dd-tabs--headings` is the strip as a row of section headings, for a public
   page (#267): the open tab is the page's own heading, in full ink behind the
   brand dot; the rest are a step smaller and muted behind a faint one, and
   when the tabs carry `data-magnetic` they lean toward the pointer, the open
   one at half the pull. No hairline, no underline. A strip of one tab reads exactly as the heading it replaced.
   ============================================================ */
.dd-tabs__nav {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-6);
    margin-bottom: var(--space-6);
    border-bottom: 1px solid var(--border);
}

.dd-tabs__tab {
    position: relative;
    padding: 0 0 var(--space-3);
    background: none;
    border: 0;
    cursor: pointer;
    font-family: var(--font-display);
    font-size: 1.0625rem;
    line-height: var(--lh-snug);
    color: var(--text-muted);
    transition: color var(--dur-base) var(--ease-soft);
}
.dd-tabs__tab:hover { color: var(--text); }
.dd-tabs__tab.is-active { color: var(--text); }

/* sliding coral underline indicator that sits on the nav hairline */
.dd-tabs__tab::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 2px;
    border-radius: var(--radius-pill);
    background: var(--accent-strong);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--dur-base) var(--ease-dreamy);
}
.dd-tabs__tab.is-active::before { transform: scaleX(1); }

.dd-tabs__tab:focus-visible {
    outline: var(--focus-outline);
    outline-offset: 3px;
    border-radius: var(--radius-sm);
}

.dd-tabs__panel[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
    .dd-tabs__tab,
    .dd-tabs__tab::before { transition: none; }
}

/* ---- the strip as headings (#267) ----------------------------------
   The size and face of a portfolio block heading, so a strip sits where a
   heading did without changing the page's rhythm. The dot is the underline's
   pseudo-element taken over: the brand's mark for "this one", on every tab.

   The open tab is the heading; the others are a step smaller, sat on its
   baseline, and alive under the pointer (owner, 2026-09-25): they lean toward
   it (`data-magnetic`, js/components.js, which skips touch and reduced motion)
   and their dot blooms, the brand's hover (design-language §5). The open tab
   leans too, at half the pull (`--dd-magnetic`, which the magnet reads on every
   move, so it follows the tab that is open now). A strip of one tab carries
   no magnet at all: a heading that moves is not a choice. A tab grows into its
   size as it is chosen, so the strip reflows as the choice moves rather than
   jumping. */
.dd-tabs--headings .dd-tabs__nav {
    align-items: baseline;
    column-gap: var(--space-5);
    row-gap: var(--space-2);
    margin-bottom: var(--space-3);
    border-bottom: 0;
}
.dd-tabs--headings .dd-tabs__tab {
    display: inline-flex;
    align-items: center;
    padding: 0;
    font-size: 17px;
    transition: color var(--dur-base) var(--ease-soft),
                font-size var(--dur-base) var(--ease-dreamy),
                transform var(--dur-slow) var(--ease-dreamy);
}
.dd-tabs--headings .dd-tabs__tab.is-active {
    --dd-magnetic: 0.1;
    font-size: 20px;
}
.dd-tabs--headings .dd-tabs__tab::before {
    position: static;
    flex: none;
    width: 0.4em;
    height: 0.4em;
    margin-right: 0.5em;
    border-radius: 50%;
    background: var(--line);
    transform: none;
    transition: background-color var(--dur-base) var(--ease-soft),
                transform var(--dur-base) var(--ease-dreamy);
}
.dd-tabs--headings .dd-tabs__tab:not(.is-active):hover::before {
    background: var(--accent);
    transform: scale(1.5);
}
.dd-tabs--headings .dd-tabs__tab:not(.is-active):active::before {
    transform: scale(1.1);
}
.dd-tabs--headings .dd-tabs__tab.is-active::before {
    background: var(--accent);
    transform: none;
}
@media (max-width: 480px) {
    .dd-tabs--headings .dd-tabs__nav { column-gap: var(--space-4); }
    .dd-tabs--headings .dd-tabs__tab { font-size: 15px; }
    .dd-tabs--headings .dd-tabs__tab.is-active { font-size: 17px; }
}
@media (prefers-reduced-motion: reduce) {
    .dd-tabs--headings .dd-tabs__tab,
    .dd-tabs--headings .dd-tabs__tab::before { transition: none; }
    .dd-tabs--headings .dd-tabs__tab:not(.is-active):hover::before,
    .dd-tabs--headings .dd-tabs__tab:not(.is-active):active::before { transform: none; }
}

/* ---- a tab whose panel cannot do its job yet (#199) ----------------
   Nothing here announces it. A tab should not call your attention unless you
   are already trying to interact with it (owner, 2026-08-31), so the label is
   an ordinary label and the whole message lives in the panel — the reader
   meets it at the moment they went looking, and not before.

   What is left is a *selected* state that agrees with what you are then
   reading: the tab does not take the promotion to full ink, and its underline
   comes back in the attention colour instead of coral. It never dims below the
   muted stop its siblings wear — a control somebody is meant to click has to
   meet AA, and the exemption for inactive controls does not cover one that
   works. It is not `disabled` and carries no `aria-disabled` either: those
   describe a control that does nothing, and everything in the panel behind
   this one still works. */
.dd-tabs__tab--gated.is-active {
    color: var(--text-muted);
}
.dd-tabs__tab--gated.is-active::before {
    background: var(--attention);
}
