/* ── Radius scale (design system) ────────────────────────────────────
   Radius is opt-in and lives on a 3-step scale. Keep these values in sync
   with the Tailwind `borderRadius` tokens in build.py (control/card/panel).
     --r-sm  controls        — inputs, buttons
     --r-md  attached surface — the router results popover, small cards
     --r-lg  large surface    — hero strip, menu panel
   Non-control elements are square by default, so nothing rounds unless a
   component rule below or a `rounded-*` utility asks for it. Native form
   controls carry an intrinsic radius on some platforms — zero it here. */
  :root{ --r-sm:.5rem; --r-md:.75rem; --r-lg:1.5rem;
         /* ── palette ──────────────────────────────────────────
            Mirrors the Tailwind config in build.py so the SAME six values are
            reachable from plain CSS. Before this existed, site.css referenced
            var(--rule) which was never defined anywhere: the fallback was doing
            all the work and the file only looked tokenised.
            Keep in sync with build.py `colors:` until the Oxygen port makes
            one of the two the single source. */
         --paper:  #FAFAFA;
         --ink:    #111110;
         --muted:  #6B6763;
         --rule:   #DDDAD5;
         --maroon: #6A1312;
         --maroonD:#511110;
         /* the "recede" value shared by #area-cycle and the overlay menu */
         --recede: #C9C4BF;
         /* ── motion tokens (the whole system tunes from here) ──
            --ease-out is an expo-out decel: strong start, long soft settle.
            That long tail is what reads as "expensive" vs a plain ease. */
         --ease-out:  cubic-bezier(.16,1,.3,1);
         --ease-line: cubic-bezier(.22,1,.32,1);
         --dur-hero:  .85s;
         --dur-sec:   .72s;
         --dur-child: .58s;
         --dur-line:  .78s;
         --rise:      clamp(20px, 2.6vw, 34px);   /* section-level travel */
         --stagger:   80ms; }
  button, input, select, textarea { border-radius: 0; }
  html { scroll-behavior: smooth; }
  body { -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }
  .u-link { background-image: linear-gradient(currentColor, currentColor);
            background-size: 0% 1px; background-repeat: no-repeat;
            background-position: 0 100%; transition: background-size .25s ease; }
  .u-link:hover, .u-link:focus-visible { background-size: 100% 1px; }
  :focus-visible { outline: 2px solid #6A1312; outline-offset: 3px; }

  /* Last-resort flush. The inline head script adds this when site.js never
     reported for duty, so a failed script degrades to a fully visible page
     instead of a blank one. Must out-specify `.js-on .reveal{opacity:0}`. */
  html.reveal-flush .reveal,
  html.reveal-flush .reveal-sm,
  html.reveal-flush .reveal-stagger > *,
  html.reveal-flush .reveal-lines .rl-in { opacity:1 !important; transform:none !important; }
  html.reveal-flush [data-type-q]        { visibility:visible !important; }
  /* Timeline spines. `.reveal-stagger > *` above already catches .spine-draw
     because it is a direct child of the <ol>, but .spine-conn and .spine-seg sit
     INSIDE a .reveal rather than being one, so the flush never reached them:
     with site.js dead the connector stayed at scaleY(0) and the timeline line
     broke above the MADV disc. Verified 2026-07-30 by removing site.js and
     leaving the inline failsafe intact. */
  html.reveal-flush .spine-conn,
  html.reveal-flush .spine-seg           { transform:none !important; }
  /* the equipa profile panels wait on .is-open, which only site.js ever sets */
  html.reveal-flush .team-panel-in,
  html.reveal-flush .team-panel-in > div > div > div,
  html.reveal-flush .team-panel-in > div > div { opacity:1 !important; transform:none !important; }

  /* ══ MOTION LAYER ══════════════════════════════════════════════════
     Principle: animation is ENHANCEMENT, never a gate.
     Every element's base state is visible + correct. If JS is off, if
     animation-timeline is unsupported, or if the user prefers reduced
     motion, the page renders complete and static. Nothing sits at
     opacity:0 waiting to be rescued (the flaw in all 16 peer sites).
     ════════════════════════════════════════════════════════════════ */

  @keyframes rise      { from { opacity:0; transform: translateY(14px) } to { opacity:1; transform:none } }
  @keyframes riseSmall { from { opacity:0; transform: translateY(8px)  } to { opacity:1; transform:none } }
  @keyframes wipeIn    { from { clip-path: inset(0 100% 0 0) }          to { clip-path: inset(0 0 0 0) } }
  @keyframes ruleGrow  { from { transform: scaleY(0) }                   to { transform: scaleY(1) } }

  @media (prefers-reduced-motion: no-preference) {

    /* ── On-load hero entrance ──────────────────────────────────────
       Uses a transition toggled by a class, NOT a CSS animation.
       A running CSS animation on an element overlaying the WebGL canvas
       promotes it to a compositing layer and produces a black artifact
       over the canvas (reproduced: disabling the animations removed it).
       A transition that has finished leaves no persistent layer.        */
    .anim-load      { opacity: 0; transform: translateY(16px);
                      transition: opacity var(--dur-hero) var(--ease-out),
                                  transform var(--dur-hero) var(--ease-out); }
    html.ready .anim-load { opacity: 1; transform: none; }
    /* .anim-load is a TRANSITION (see note above), so the stagger must use
       transition-delay — animation-delay is inert here and left it un-cascaded. */
    .anim-load-d1   { transition-delay: .06s }
    .anim-load-d2   { transition-delay: .14s }
    .anim-load-d3   { transition-delay: .22s }
    .anim-load-d4   { transition-delay: .30s }
    .anim-load-d5   { transition-delay: .40s }

    /* Router question types in as the last hero beat. Hidden until the
       typewriter runs (js-on set synchronously in <head>, so no flash);
       min-height reserves the line so typing causes no layout shift. No JS ->
       full question shown. Reduced motion -> shown in full, no typing. */
    [data-type-q]        { min-height: 1.5em; }
    html.js-on [data-type-q]         { visibility: hidden; }
    html.js-on [data-type-q].type-on { visibility: visible; }
    .type-cursor         { display: inline-block; width: .5ch; margin-left: 1px;
                           font-weight: 300; opacity: .8; }

    .anim-wipe      { animation: wipeIn 1.05s cubic-bezier(.22,.9,.3,1) both; }
    .anim-rule      { animation: ruleGrow .9s cubic-bezier(.22,.9,.3,1) .25s both; transform-origin: top; }

    /* ── Scroll reveal: ONE-SHOT, JS-gated.
       The hidden state is added BY JS (.js-on). No JS -> never hidden.
       Reduced motion -> JS opts out. Once revealed, stays revealed
       (no re-animating on every scroll pass, which reads as fidgety). ── */
    .js-on .reveal         { opacity: 0; transform: translateY(var(--rise));
                             transition: opacity var(--dur-sec) var(--ease-out),
                                         transform var(--dur-sec) var(--ease-out); }
    .js-on .reveal-sm      { opacity: 0; transform: translateY(14px);
                             transition: opacity var(--dur-sec) var(--ease-out),
                                         transform var(--dur-sec) var(--ease-out); }
    .js-on .reveal-stagger > * { opacity: 0; transform: translateY(16px);
                             transition: opacity var(--dur-child) var(--ease-out),
                                         transform var(--dur-child) var(--ease-out); }
    .js-on .is-in,
    .js-on .is-in > *      { opacity: 1 !important; transform: none !important; }

    /* ── Masked line-reveal (the award-site signature) ──────────────────
       Each line is clipped by an overflow-hidden wrapper; the inner sits
       one line-height BELOW and slides up into view, staggered per line.
       Structure: .reveal-lines > .rl-line > .rl-in (see _home.main.html).
       Base state (no .js-on) leaves .rl-in in place, so no-JS / reduced
       motion show the heading complete and static. The padding/margin pair
       gives descenders and italics room so the clip never crops a glyph. */
    .js-on .reveal-lines .rl-line { display: block; overflow: hidden;
                                    padding-bottom: .12em; margin-bottom: -.12em; }
    .js-on .reveal-lines .rl-in   { display: inline-block; transform: translateY(115%);
                                    transition: transform var(--dur-line) var(--ease-line); }
    .js-on .reveal-lines.is-in .rl-in            { transform: none; }
    .js-on .reveal-lines.is-in .rl-line:nth-child(2) .rl-in { transition-delay: .10s; }
    .js-on .reveal-lines.is-in .rl-line:nth-child(3) .rl-in { transition-delay: .20s; }
    .js-on .reveal-lines.is-in .rl-line:nth-child(4) .rl-in { transition-delay: .30s; }
    /* the generic is-in reset targets direct children (.rl-line); keep it from
       yanking the clipped inner to its final spot before the transition runs. */
    .js-on .reveal-lines.is-in .rl-in            { opacity: 1; }

    /* ── Line-draw: the timeline spine and the MADV connector grow DOWNWARD
       (transform-origin top) as the points cascade in, instead of fading.
       They opt out of the generic reveal above — which would snap them to
       transform:none — by carrying their own scaleY, !important where it must
       beat the generic is-in reset. JS-gated + reduced-motion-safe like the
       rest (no .js-on -> full-height line, no animation). */
    .js-on .reveal-stagger > .spine-draw {
      opacity: 1; transform: scaleY(0); transform-origin: top center;
      transition: transform 1s var(--ease-out);
    }
    .js-on .reveal-stagger.is-in > .spine-draw { transform: scaleY(1) !important; }

    .js-on .spine-conn {
      transform: scaleY(0); transform-origin: top center;
      transition: transform .65s var(--ease-out);
    }
    .js-on .reveal.is-in .spine-conn { transform: scaleY(1); }

    /* ── /a-sociedade/ timeline: the spine is segmented per milestone, so it
       draws PROGRESSIVELY as each <li> reveals on scroll rather than the whole
       line arriving at once. Five long milestones need that; the homepage's four
       one-liners do not.
       The !important and the 4-class specificity are both required: line 127
       (`.js-on .is-in > *`) forces transform:none !important on every direct
       child of a revealed element, which would flatten the draw to an instant
       full-height line. (0,4,0) beats its (0,2,0). */
    .js-on .reveal .spine-seg {
      transform: scaleY(0); transform-origin: top center;
      transition: transform .7s var(--ease-out);
    }
    .js-on .reveal.is-in > .spine-seg { transform: scaleY(1) !important; }

    /* ── Micro-interaction: area rows nudge on hover ── */
    #areas li a { transition: color .2s ease, padding-left .28s cubic-bezier(.22,.9,.3,1); }
    #areas li a:hover, #areas li a:focus-visible { padding-left: 8px; }
  }

  /* Counter: the real value lives in the HTML. JS only animates toward it. */
  .tabular { font-variant-numeric: tabular-nums; }

  /* ── Team carousel: CSS-only horizontal scroll-snap. Thin scrollbar as the
     scroll affordance; snap for tidy stops. -webkit-overflow-scrolling for
     momentum on touch. ── */
  .team-carousel { -webkit-overflow-scrolling: touch; scrollbar-width: thin;
                   scrollbar-color: #DDDAD5 transparent; scroll-padding-left: 1.5rem; }
  .team-carousel::-webkit-scrollbar { height: 6px; }
  .team-carousel::-webkit-scrollbar-track { background: transparent; }
  .team-carousel::-webkit-scrollbar-thumb { background: #DDDAD5; border-radius: 3px; }
  @media (min-width:1024px){ .team-carousel { scroll-padding-left: 4rem; } }

/* ── corner brackets (RRE detailing) ── */
.corner::before, .corner::after,
.corner > .c3, .corner > .c4 { content:""; position:absolute; width:9px; height:9px; }
.corner::before { top:0; left:0;  border-top:1px solid currentColor; border-left:1px solid currentColor; }
.corner::after  { top:0; right:0; border-top:1px solid currentColor; border-right:1px solid currentColor; }
.corner .c3     { bottom:0; left:0;  border-bottom:1px solid currentColor; border-left:1px solid currentColor; }
.corner .c4     { bottom:0; right:0; border-bottom:1px solid currentColor; border-right:1px solid currentColor; }

/* ── practice-area cycle list (Harvey) ──────────────────────────────
   No JS: every item is fully legible. JS dims the inactive ones and
   cycles focus, so nothing is ever hidden behind the effect.        */
#area-cycle a { color: #111110; display: block; padding: 2px 0; }
.js-on #area-cycle a { color: var(--recede); transition: color .55s ease; }
.js-on #area-cycle li.is-active a { color: #111110; }
#area-cycle a:hover, #area-cycle a:focus-visible { color: #6A1312 !important; }

/* ── hero field ────────────────────────────────────────────────────
   The shader paints into a canvas it appends here. Without JS we fall
   back to the baked texture (see <noscript> in the shell) with a slow
   CSS drift; the script neutralises that drift when it takes over.   */
@keyframes heroDrift{
  from{transform:translate3d(-2%,-1%,0) scale(1.12)}
  to  {transform:translate3d(3%,2%,0)   scale(1.20)}
}

/* ══ SHADER MASK ═══════════════════════════════════════════════════
   A masked numeral/word: the hero field shown through the glyphs, maroon
   only inside the letters (the accent-only device). Knockout layering, so
   the live canvas is never itself masked (see mask-field.js):
     .mask-fill  maroon solid   — the no-WebGL / no-JS fallback
     canvas      live field     — inserted between fill and cut
     .mask-cut   paper knockout — paper everywhere except the glyph holes
   ⚠ .mask-cut rect fill MUST equal the host section's background, or the
     area around the glyphs will not disappear. Currently paper (#FAFAFA). */
.mask{ position:relative; width:clamp(9rem,22vw,15rem); aspect-ratio:440/360; }
.mask-fill{ position:absolute; inset:0; background:#6A1312; }
.mask canvas{ position:absolute; inset:0; width:100%; height:100%; display:block; }
.mask-cut{ position:absolute; inset:0; width:100%; height:100%; display:block; }
/* Direct child only — must NOT recolour the mask's inner white rect, whose
   luminance has to stay 1 (pure #fff) or the knockout goes translucent and
   the layer behind bleeds a faint box. */
.mask-cut > rect{ fill:#FAFAFA; }
.mask-text{ font-family:"Fraunces",serif; font-weight:600; }

/* Promise triangle cluster: desktop sits on the right; below lg it moves to the
   top-centre (resized) so it isn't cropped and reads above the statement. */
.cluster-mob{ display:none; }
@media (max-width:1023px){
  .cluster-desk{ display:none; }
  .cluster-mob{ display:inline; }
}

/* areas-index inline filter: hidden until area-filter.js marks it ready, so
   no-JS renders the full list with no orphaned input. */
[data-area-filter]{ display:none; }
[data-area-filter].is-ready{ display:block; }
/* same contract for the publicações author filter */
[data-post-filter]{ display:none; }
[data-post-filter].is-ready{ display:block; }
/* The [hidden] ATTRIBUTE is a UA-stylesheet rule, so any Tailwind display
   utility on the same element outranks it and the element stays on screen.
   Bit twice now: #contact-modal (guarded above) and these two.
     · the clear button carries inline-flex and rendered while hidden
     · a post list carries grid and is hidden at RUNTIME, so a static scan of
       the built HTML cannot catch it — only a rendered check can
   Always verify with offsetParent/computed display, never with el.hidden. */
[data-filter-clear][hidden],
[data-post-list][hidden]{ display:none !important; }

/* Featured post — ONLY while the archive is unfiltered. Once a facet is on,
   every result renders at the same size: a single double-width card among the
   matches reads as a different kind of object rather than one of N results,
   which is exactly what it looked like when a filter left only the lead.
   The 16/7 crop is a desktop device — at 390px it collapses to a ~129px
   letterbox, so mobile keeps the normal 3/2 either way. */
[data-post-list]:not(.is-filtering) [data-featured]{ grid-column: 1 / -1; }
[data-post-list]:not(.is-filtering) [data-featured] .post-title{
  font-size: clamp(1.6rem, 3vw, 2.3rem); line-height: 1.15; }
@media (min-width: 768px){
  [data-post-list]:not(.is-filtering) [data-featured] .post-img{ aspect-ratio: 16 / 7; }
}

/* field panel: the live hero field shown directly (no knockout) as a band/box.
   The maroon gradient on the element is the no-WebGL / no-JS fallback; the
   canvas covers it when the shader runs. */
.field-panel{ position:relative; overflow:hidden;
  background:radial-gradient(130% 220% at 22% -10%, #7a1614 0%, #3a0d0b 46%, #120302 100%); }
.field-panel canvas{ position:absolute; inset:0; width:100%; height:100%; display:block; }

/* square mask (for the triad shape); base .mask defaults to the numeral aspect */
.mask--sq{ aspect-ratio:1; }

/* ── outer-rounded (concave) corners where the white block meets the field
   gutters. The block's bottom corners stay square; instead a paper fillet fills
   the gutter's bottom corner so the FIELD reads as an outer-rounded corner. Pure
   CSS via a radial-gradient pseudo-element (transparent inside the arc = field
   shows; paper outside = the concave fillet). Attached to the block's parent so
   it can sit OUTSIDE the block's overflow:hidden, in the gutter. */
.gutter-round{ position:relative; }
.gutter-round::before,
.gutter-round::after{
  content:""; position:absolute; bottom:0; width:1.5rem; height:1.5rem;
  pointer-events:none;
}
.gutter-round::before{                    /* left gutter, bottom-right corner */
  left:-1.5rem;
  background:radial-gradient(circle 1.5rem at top left, #0000 98%, #FFFFFF 100%);
}
.gutter-round::after{                     /* right gutter, bottom-left corner */
  right:-1.5rem;
  background:radial-gradient(circle 1.5rem at top right, #0000 98%, #FFFFFF 100%);
}

/* ── triangle-mesh accent (the client's signature motif, distilled) ─────
   Their triangle grid, in maroon, faded to nothing at one edge, BEHIND
   content. USE RARELY — a texture, never a full background. Put .tri-mesh on
   a container; it paints a faded ::before behind its (positioned) children.
   Fade angle via --tri-fade (default fades out to the left). */
.tri-mesh{ position:relative; }
.tri-mesh > *{ position:relative; z-index:1; }
.tri-mesh::before{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none; opacity:.32;
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='34'%20height='30'%3E%3Cpolygon%20points='17,3%2032,27%202,27'%20fill='none'%20stroke='%236A1312'%20stroke-width='1'/%3E%3C/svg%3E");
  background-size:34px 30px;
  -webkit-mask-image:linear-gradient(var(--tri-fade,270deg),#000,transparent 65%);
          mask-image:linear-gradient(var(--tri-fade,270deg),#000,transparent 65%);
}

/* ══ MENU PANEL ════════════════════════════════════════════════════
   Cucinelli pattern, mirrored to the right.
   No JS  -> #menu-panel is a normal block of links at the end of the
             document. Everything reachable, nothing hidden.
   JS     -> becomes a fixed overlay that grows from the top-right.
   ══════════════════════════════════════════════════════════════════ */

/* Base (also the no-JS state): every item fully legible.
   The left padding reserves the current-page marker column on EVERY item, so
   marking one does not shove it out of alignment with the rest of the list.
   These live here rather than as utility classes on the markup because the
   `#menu-list a` ID selector outranks any Tailwind utility. */
#menu-list a { position:relative; display:block; padding:.18em 0 .18em 2.5rem; color:#111110; }

/* Current page. Colour is not the only signal: the rule below is a visible
   marker and aria-current carries it for assistive tech. `currentColor` keeps
   the marker in step with the hover dim/undim transitions further down. */
#menu-list a[aria-current="page"] { color:#6A1312; }
#menu-list a[aria-current="page"]::before{
  content:""; position:absolute; left:0; top:50%;
  width:1.5rem; height:1px; background:currentColor;
}

/* ---- JS-enhanced overlay ---- */
.js-on #menu-panel{
  position:fixed; top:.75rem; right:.75rem; bottom:.75rem; z-index:50;
  width:min(30rem, calc(100vw - 1.5rem));
  /* floated off the edges so the radius is visible; full border, not just left */
  border:1px solid #DDDAD5;
  border-radius:var(--r-lg);
  overflow-y:auto; overscroll-behavior:contain;
  /* grow from the top-right corner. The `round` keyword keeps the clip
     rectangle's corners rounded — a plain inset() clips to a sharp rect
     and would square off border-radius through the whole animation. */
  clip-path: inset(0 0 100% 100% round var(--r-lg));
  visibility:hidden;
  transition: clip-path .62s cubic-bezier(.22,.9,.26,1), visibility 0s linear .62s;
}
.js-on #menu-panel.is-open{
  clip-path: inset(0 0 0 0 round var(--r-lg));
  visibility:visible;
  transition: clip-path .62s cubic-bezier(.22,.9,.26,1), visibility 0s;
}
.js-on #menu-close{ display:flex; }
.js-on #menu-backdrop:not([hidden]){ opacity:1; }

/* items fade in just behind the panel edge */
.js-on #menu-panel .menu-inner > *{
  opacity:0; transform:translateY(10px);
  transition:opacity .5s ease, transform .5s cubic-bezier(.22,.9,.26,1);
}
.js-on #menu-panel.is-open .menu-inner > *{ opacity:1; transform:none; }
.js-on #menu-panel.is-open .menu-inner > *:nth-child(2){ transition-delay:.16s }
.js-on #menu-panel.is-open .menu-inner > *:nth-child(3){ transition-delay:.24s }

/* the Cucinelli move: hovering one item recedes the others */
/* Hover treatment mirrors the homepage practice-area list (#area-cycle):
   same 0.55s ease, same dim value, hovered item goes MAROON not ink.    */
.js-on #menu-list a{ transition: color .55s ease; }
/* The current page is EXEMPT from the recede. Without :not() these selectors
   (0,2,1) outrank #menu-list a[aria-current] (0,1,1), so the one item the user
   needs to locate dims to 1.66:1 the moment the pointer enters the list, and
   the ::before marker follows it via currentColor. */
.js-on #menu-list:hover a:not([aria-current="page"]),
.js-on #menu-list:focus-within a:not([aria-current="page"]){ color:var(--recede); }
.js-on #menu-list a:hover,
.js-on #menu-list a:focus-visible{ color:#6A1312; }

/* hamburger -> close */
#menu-toggle .mt-bar{ transition:transform .4s cubic-bezier(.22,.9,.26,1), opacity .3s ease; }
#menu-toggle[aria-expanded="true"] .mt-bar-1{ transform:translateY(6px) rotate(45deg); }
#menu-toggle[aria-expanded="true"] .mt-bar-2{ opacity:0; transform:translateY(-50%) scaleX(.4); }
#menu-toggle[aria-expanded="true"] .mt-bar-3{ transform:translateY(-6px) rotate(-45deg); }

body.menu-open{ overflow:hidden; }

@media (prefers-reduced-motion: reduce){
  .js-on #menu-panel,
  .js-on #menu-panel .menu-inner > *,
  #menu-toggle .mt-bar { transition-duration:.01ms !important; }
}

/* ── menu backdrop: subtle blur behind the panel ──────────────────
   Driven by .is-on (not inline opacity) so the blur and the dim fade
   together. -webkit- prefix for Safari.                             */
#menu-backdrop{
  -webkit-backdrop-filter: blur(0px);
          backdrop-filter: blur(0px);
  transition: opacity .5s ease,
              -webkit-backdrop-filter .5s ease,
                      backdrop-filter .5s ease;
}
#menu-backdrop.is-on{
  opacity: 1;
  -webkit-backdrop-filter: blur(5px);
          backdrop-filter: blur(5px);
}
@media (prefers-reduced-motion: reduce){
  #menu-backdrop{ transition-duration: .01ms !important; }
}

/* ── transparent header, desktop only, dark-hero pages ─────────────
   Below md the header stays solid and in flow (legibility on small
   screens matters more than the effect).                            */
@media (min-width: 768px){
  .site-header[data-header="overlay"]{
    position:absolute; top:0; left:0; right:0;
    background:transparent;
    border-bottom-color:transparent;
  }
  /* lift the contents onto the dark field */
  .site-header[data-header="overlay"] a[aria-label]{ color:#FAFAFA; }
  .site-header[data-header="overlay"] #menu-toggle{ color:#FAFAFA; }
  .site-header[data-header="overlay"] .lang-btn{ color:#FAFAFA; }
  .site-header[data-header="overlay"] .lang-menu{ background:#FAFAFA; }
  .site-header[data-header="overlay"] #menu-toggle .mt-bar{ background:#FAFAFA; }
  /* the hero already bottom-aligns its content; just guarantee clearance */
  [data-header="overlay"] ~ main .hero-field{ top:0; }
}

/* ── language switcher ─────────────────────────────────────────────
   Dummy for now. Globe is inline SVG on currentColor so it inherits
   the header variant (light over the hero, ink elsewhere).           */
.lang-btn{ color:inherit; }
.lang-globe{ overflow:visible; }
.lang-globe circle,
.lang-globe path{ opacity:.75; }

/* Subtle "rotation": the meridian's width eases in and out, which reads
   as the globe turning without actually rotating anything. */
.lang-meridian{
  transform-origin: 10px 10px;
  transition: transform 1.15s cubic-bezier(.42,0,.32,1), opacity .5s ease;
  opacity:.85;
}
/* meridian sweeps through edge-on and back out the other side: reads as
   the globe turning, without rotating (and without a repeating loop). */
/* scaleX(-1) was a no-op: mirroring a symmetric ellipse looks identical.
   Hold a turned position instead, so rest and hover clearly differ. */
.lang:hover .lang-meridian,
.lang:focus-within .lang-meridian{ transform: scaleX(.22); opacity:1; }
.lang-globe{ transition: transform 1.15s cubic-bezier(.42,0,.32,1); }
.lang:hover .lang-globe,
.lang:focus-within .lang-globe{ transform: rotate(-12deg); }

.lang-current{ opacity:.85; transition:opacity .3s ease; }
.lang:hover .lang-current{ opacity:1; }

/* dropdown: closed by default once JS is on; plain list without JS */
.lang-menu{ display:none; z-index:60; }
.js-on .lang-menu{ display:none; }
.js-on .lang.is-open .lang-menu{ display:block; }
html:not(.js-on) .lang-menu{
  display:flex; position:static; border:0; background:none; margin:0; padding:0;
}
html:not(.js-on) .lang-menu a{ padding:.5rem .6rem; }

@media (prefers-reduced-motion: reduce){
  .lang-meridian{ transition-duration:.01ms !important; }
}

/* ── scroll header ─────────────────────────────────────────────────
   One header, three states:
     natural  — as authored (overlay on the homepage, in-flow elsewhere)
     is-fixed — pinned, solid, ink contents (it now sits over content)
     is-hidden— pinned but translated out (scrolling down)
   No duplicate markup, so there is still exactly one #menu-toggle.   */
.js-on .site-header{
  transition: transform .45s cubic-bezier(.22,.9,.26,1),
              background-color .3s ease, border-color .3s ease;
  will-change: transform;
}
.js-on .site-header.is-fixed{
  position:fixed; top:0; left:0; right:0; z-index:45;
  background:#FAFAFA; border-bottom-color:#DDDAD5;
  /* soft drop so the pinned bar reads as detached over paper sections
     (Continuidade, Publicações), where the hairline border alone is faint. */
  box-shadow:0 6px 22px -16px rgba(17,17,16,.28);
}
.js-on .site-header.is-fixed.is-hidden{ transform:translateY(-100%); }

/* pinned bar is always solid, so overlay pages must drop back to ink */
.js-on .site-header.is-fixed[data-header="overlay"] a[aria-label],
.js-on .site-header.is-fixed[data-header="overlay"] #menu-toggle,
.js-on .site-header.is-fixed[data-header="overlay"] .lang-btn{ color:#111110; }
.js-on .site-header.is-fixed[data-header="overlay"] #menu-toggle .mt-bar{ background:#111110; }

@media (prefers-reduced-motion: reduce){
  .js-on .site-header{ transition-duration:.01ms !important; }
}

/* ══ PRACTICE-AREA ROUTER ══════════════════════════════════════════
   Lives in the hero, over the dark field. Without JS the input is
   simply absent from the flow's usefulness — the two links below it
   still reach the areas index and contact, so nothing dead-ends.   */
.router-field{
  display:flex; align-items:center; gap:.75rem;
  background:rgba(242,241,241,.10);
  border:1px solid rgba(242,241,241,.28);
  border-radius:var(--r-sm);
  padding:.95rem 1.15rem;
  backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
  transition:border-color .3s ease, background-color .3s ease;
}
.router-field:focus-within{ border-color:rgba(242,241,241,.75); background:rgba(242,241,241,.16); }
.router-icon{ color:rgba(242,241,241,.55); flex:none; }
.router-field input{
  flex:1 1 auto; min-width:0; background:none; border:0; outline:0;
  color:#FAFAFA; font-size:15px; letter-spacing:.01em;
}
.router-field input::placeholder{ color:rgba(242,241,241,.45); }

[data-router-results]{
  position:absolute; left:0; right:0; top:calc(100% + .5rem); z-index:30;
  background:#FAFAFA; border:1px solid #DDDAD5;
  border-radius:var(--r-md);
  /* overflow:hidden clips the first/last item's hover fill to the rounded
     corners. Results are capped at 5 short rows, so this never needs to
     scroll — the max-height is only a guard. */
  max-height:22rem; overflow:hidden;
}
[data-router-results] li + li{ border-top:1px solid #DDDAD5; }
[data-router-results] a{
  display:flex; align-items:baseline; justify-content:space-between; gap:1rem;
  padding:.85rem 1.15rem; color:#111110; transition:background-color .2s ease, color .2s ease;
}
[data-router-results] a:hover,
[data-router-results] a[aria-selected="true"]{ background:#111110; color:#FAFAFA; }
.router-area{ font-size:15px; line-height:1.3; }
.router-lawyer{ font-size:11px; letter-spacing:.14em; text-transform:uppercase; opacity:.55; white-space:nowrap; }
.router-empty{ padding:1rem 1.15rem; color:#6B6763; font-size:14px; line-height:1.6; }
.router-empty strong{ color:#111110; }
.router-empty-cta{ margin-top:.4rem; }
.router-empty a{ color:#6A1312; text-decoration:underline; text-underline-offset:3px; }

/* no JS -> hide the input, keep the links */
html:not(.js-on) .router-field{ display:none; }

/* ── router, light variant (variant B: input sits on paper) ────────── */
.router--light .router-field{
  background:rgba(17,17,16,.03);
  border-color:#DDDAD5;
  backdrop-filter:none; -webkit-backdrop-filter:none;
}
.router--light .router-field:focus-within{ border-color:#111110; background:rgba(17,17,16,.05); }
.router--light .router-icon{ color:#6B6763; }
.router--light .router-field input{ color:#111110; }
.router--light .router-field input::placeholder{ color:#9A948E; }

/* ══ SCROLL CUE ════════════════════════════════════════════════════
   A hairline with a single drop travelling down it — same vocabulary
   as the rest of the rules on the page, no bouncing chevron.
   Revealed by JS a few seconds after load (class .cue-on). NOTE the
   class is deliberately NOT .is-in: the scroll-reveal system declares
   `.js-on .is-in { opacity:1 !important }`, which silently overrode the
   fade-out here.  So it never
   competes with the hero's own entrance. Hidden once the user scrolls.
   ══════════════════════════════════════════════════════════════════ */
.scroll-cue{
  opacity:0; transform:translateY(-4px);
  transition:opacity 1.1s var(--ease-out), transform 1.1s var(--ease-out);
}
.scroll-cue.cue-on{ opacity:1; transform:none; }
.scroll-cue.cue-off{ opacity:0; transform:translateY(4px); transition-duration:.5s; }

.scroll-cue__track{
  display:block; position:relative; width:1px; height:56px;
  background:linear-gradient(180deg, rgba(17,17,16,.14) 0%, rgba(17,17,16,.04) 100%);
  overflow:hidden;
}
.scroll-cue__drop{
  position:absolute; left:0; top:0; width:1px; height:16px;
  background:linear-gradient(180deg, transparent 0%, #6A1312 55%, transparent 100%);
}
@media (prefers-reduced-motion: no-preference){
  .scroll-cue.cue-on .scroll-cue__drop{ animation:scrollDrop 2.6s cubic-bezier(.55,0,.45,1) infinite; }
}
@keyframes scrollDrop{
  0%   { transform:translateY(-16px); opacity:0 }
  18%  { opacity:1 }
  72%  { opacity:1 }
  100% { transform:translateY(56px); opacity:0 }
}

/* ── quick-contact lightbox ──────────────────────────────────────────
   Centred dialog opened by [data-contact-open]. Base state is fully
   visible; JS toggles .is-on to run the fade/scale. The `hidden`
   attribute (removed by JS on open) keeps it out of the no-JS render. */
body.contact-open{ overflow:hidden; }

/* The `flex` utility on #contact-modal is author CSS and would otherwise beat
   the UA stylesheet's [hidden]{display:none}, leaving a full-screen overlay
   that swallows every click. Re-assert hidden from the author layer. */
#contact-modal[hidden], #contact-backdrop[hidden]{ display:none !important; }

#contact-backdrop{ opacity:0; transition:opacity .4s ease; }
#contact-backdrop.is-on{ opacity:1; }

#contact-modal .contact-card{
  opacity:0; transform:translateY(12px) scale(.985);
  transition:opacity .34s ease, transform .34s var(--ease-out);
}
#contact-modal.is-on .contact-card{ opacity:1; transform:none; }
/* heading is focused only to move screen-reader attention on success; it is
   not interactive, so the visible box outline is noise. */
#contact-modal [data-contact-done] h3:focus{ outline:none; }

/* form controls: the reset strips radius, so restore it here */
.cf-input{
  width:100%; border:1px solid var(--rule); border-radius:var(--r-sm);
  background:#fff; padding:.7rem .9rem;
  font-size:15px; line-height:1.5; color:#111110;
  outline:none; transition:border-color .18s ease, box-shadow .18s ease;
}
.cf-input::placeholder{ color:#6B6763; opacity:.7; }
.cf-input:focus{ border-color:#111110; box-shadow:0 0 0 3px rgba(17,17,16,.06); }

@media (prefers-reduced-motion: reduce){
  #contact-backdrop, #contact-modal .contact-card{ transition:none; }
}

/* ── equipa: card grid + row drawer ──────────────────────────────────
   The panel is a sibling of the card and spans the full grid width, so on a
   3-column grid it opens as a drawer UNDER THE ROW, and on a 1-column grid it
   opens directly under its own card (an accordion). Same markup, same JS.

   NO-JS / NO-CSS-JS CONTRACT: panels are visible here by default. site.js is
   what collapses them, and the toggle button is hidden until JS exists — so
   with scripting off the page is a complete, readable directory.            */
/* ROW-GAP MUST STAY 0. The panel is a grid item, so the moment it stops being
   display:none the grid inserts a row — and a row-gap would appear at full size
   instantly, jumping the page ~40px before the height animation has run a single
   frame. Row spacing therefore lives on the cards, where it is constant whether
   a panel is open or not; the panel's own spacing is padding INSIDE the animated
   height. */
.team-grid{ display:grid; grid-template-columns:1fr; gap:0 1.75rem; }
.team-card{ margin-bottom:2.5rem; }
@media (min-width:640px){ .team-grid{ grid-template-columns:repeat(2,1fr); } }
@media (min-width:1024px){ .team-grid{ grid-template-columns:repeat(3,1fr); } }

.team-panel{ grid-column:1 / -1; }
.team-card{ display:flex; flex-direction:column; align-items:flex-start; }
.team-panel-in{ padding-bottom:2.5rem; }

/* button only means something once JS can toggle */
[data-team-toggle]{ display:none; }
.js-on [data-team-toggle]{ display:inline-flex; }

.js-on .team-panel{ overflow:hidden; }
.js-on .team-panel[hidden]{ display:none; }
.js-on .team-chev{ transition:transform .3s var(--ease-out); }
.js-on [aria-expanded="true"] .team-chev{ transform:rotate(180deg); }

/* GridZoom-derived, translated into this design's register: opening a profile
   pushes the other cards back (dim + settle) so attention lands on one person,
   and that person's portrait scales up into the detail panel. No full-screen
   photo takeover — this is a law firm, the grid stays the frame. */
@media (prefers-reduced-motion: no-preference){
  .js-on .team-card{ transition:opacity .45s var(--ease-out), transform .45s var(--ease-out); }
}
/* !important + higher specificity is REQUIRED here, not laziness: the reveal
   engine's `.js-on .is-in { opacity:1 !important }` lands on every card once it
   has scrolled into view, so without this the recede silently only worked on
   cards still below the fold. */
.js-on [data-team-grid].has-open .team-card:not(.is-active){
  opacity:.34 !important; transform:scale(.985) !important; }
.js-on [data-team-grid].has-open .team-card.is-active{ opacity:1; transform:none; }
.js-on .team-card.is-active .team-card-photo{ box-shadow:0 18px 44px -28px rgba(17,17,16,.5); }
/* An open profile HOLDS the hover treatment: the pointer has usually moved down
   into the panel by then, and letting the portrait fall back to grey made the
   open card look deselected while its profile was on screen. Matches the
   homepage carousel values exactly (colour + 1.05). */
.js-on .team-card.is-active img{ filter:none; transform:scale(1.05); }
/* the whole card is the hit area, so it should read as one */
.js-on .team-card{ cursor:pointer; }
.js-on .team-card:hover .team-card-photo{ box-shadow:0 14px 34px -26px rgba(17,17,16,.42); }
.js-on .team-card:hover [data-team-toggle]{ color:var(--maroon); }

@media (prefers-reduced-motion: no-preference){
  .js-on .team-panel{ transition:height .46s var(--ease-out); }
  .js-on .team-panel-in{ opacity:0; transition:opacity .38s var(--ease-out) .06s; }
  .js-on .team-panel.is-open .team-panel-in{ opacity:1; }
  /* the zoom: the detail portrait grows from roughly the card's scale */
  .js-on .team-detail-photo{ transform:scale(.94); transform-origin:left top;
                             transition:transform .55s var(--ease-out) .04s; }
  .js-on .team-panel.is-open .team-detail-photo{ transform:none; }
  /* detail blocks settle in after the panel has opened */
  .js-on .team-panel-in > div > div > div{ opacity:0; transform:translateY(10px);
      transition:opacity .42s var(--ease-out), transform .42s var(--ease-out); }
  .js-on .team-panel.is-open .team-panel-in > div > div > div{ opacity:1; transform:none;
      transition-delay:.14s; }
}

/* Reduced motion: no recede at all. It has to match the dim rule's specificity
   AND its !important, or the escalation above silently wins and these users get
   a 34% dim with no transition to explain it. */
@media (prefers-reduced-motion: reduce){
  .js-on [data-team-grid].has-open .team-card:not(.is-active){
    opacity:1 !important; transform:none !important; }
}

/* ── tactile press feedback (physics-active-state) ───────────────────
   Buttons/CTAs get a subtle pressed scale. Two things to beat:
   1) Tailwind's .transition-colors and the reveal-stagger transition both
      set `transition` — the html.js-on prefix (higher specificity) wins so
      the transform gets a FAST .14s press curve, while opacity keeps the
      slower reveal timing.
   2) The generic is-in reset (`.is-in > * { transform:none !important }`)
      would block the pressed scale — the !important on :active with the
      html.js-on prefix out-specifies it.
   Press buttons that live inside a reveal fade in (no rise) so their
   transform stays reserved for the press. Reduced motion opts out. */
html.js-on .press, .press{
  transition: opacity var(--dur-child) var(--ease-out),
              transform .14s var(--ease-out),
              color .15s ease, background-color .15s ease, border-color .15s ease;
}
html.js-on .reveal-stagger > .press,
html.js-on .reveal > .press,
html.js-on .reveal-sm > .press{ transform: none; }
html.js-on .press:active, .press:active{ transform: scale(.97) !important; }
@media (prefers-reduced-motion: reduce){
  html.js-on .press:active, .press:active{ transform: none !important; }
}

