/* ============================================================
   ECHOSHAPE — IMAGE PROTECTION (site-wide)
   Deters casual saving/dragging of images. Loaded on every page
   via frame.css. Note: cannot fully block a determined user
   (devtools / network tab still expose files); pairs with the
   dragstart/contextmenu guard in frame.js.
   ============================================================ */
img,
picture,
svg image {
  -webkit-user-drag: none;
  -khtml-user-drag: none;
  -moz-user-drag: none;
  -o-user-drag: none;
  user-drag: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  -webkit-touch-callout: none; /* iOS: no long-press save sheet */
}

/* ============================================================
   ECHOSHAPE — FRAME COMPONENT
   The framing system that accompanies every frame on the site:
   0.5px lines, fixed 40px inset from the sides, navbar with an
   8px indicator ruler, and a bottom edge that is either a plain
   line (40px inset) or an expanded 64px toolbar.

   Usage (per page, on <body>):
     data-page-name="Frame"      → page title in the right nav cell
     data-active-nav="projects"  → nav link highlighted (products |
                                   inspirations | projects | more)
     data-home                   → home mode: hatch under the logo cell
   Include:
     <link rel="stylesheet" href="components/frame.css">
     <script src="components/frame.js" defer></script>
   ============================================================ */

:root{
  --prime:#ff6638;
  --white:#fff;
  --dark:#010001;
  --rail:rgba(255,102,56,.55);      /* legacy token — page-internal dividers */
  --frameline:#ff6638;              /* frame lines: solid #FF6638 @ 0.5px (Figma 1:1) */
  --line:0.5px;                     /* frame line weight */
  --m:40px;                         /* fixed side inset */
  --nav:64px;                       /* navbar height (Figma: 56 + 8 ruler) */
  --pb:40px;                        /* default bottom inset */
  --pbx:80px;                       /* expanded bottom bar height (with content) */
  --cell:17.6%;                     /* navbar side cell (253/1440) */
  --hover-time:.25s;
  --hover-ease:cubic-bezier(.4,0,.2,1);

  /* ---- responsive foundation (30.7.26) ----
     --fh is the height a section actually gets to work with: the frame
     minus the navbar and the bottom strip. On a phone that lands around
     600px, which is the budget every mobile composition has to fit. */
  --fh:calc(100svh - var(--nav) - var(--pb));

  /* blueprint background grid. Was pure 4.1667vw (60px @1440) and collapsed
     to ~16px on a phone; the clamp keeps the cell legible without changing
     anything at desktop width. */
  --gridsize:clamp(34px, 4.1667vw, 60px);
  --gridline:rgb(252,250,249);      /* very faint warm grid line (~delta 3-5 from white) */
  --grid:var(--gridline);           /* alias — older pages call it --grid */
  --bpgrid:
    repeating-linear-gradient(to right, var(--gridline) 0 1px, transparent 1px var(--gridsize)),
    repeating-linear-gradient(to bottom, var(--gridline) 0 1px, transparent 1px var(--gridsize));
  --bpgrid-pos:calc(var(--gridsize)*0.733) calc(var(--gridsize)*0.533);

  /* the house dash: 16px stroke / 14px gap, as a gradient rather than
     border-style:dashed (which the browser renders at its own rhythm). The site
     already does this consistently in ~20 places, each with the gradient written
     out inline; .dash-t / .dash-b below exist so new work uses the token instead
     of another copy. (An earlier note here claimed 23 native dashed borders — that
     was a miscount from grepping the word "dashed", which matches the comments.) */
  --dash:repeating-linear-gradient(to right,var(--frameline) 0 16px,transparent 16px 30px);
  /* dash weight is 1px, NOT --line. The 0.5px --line is the frame stroke (Figma
     1:1); every dashed hairline the site already draws — about's .dl, the section
     dividers, contact's panels, the footer rules — is 1px. The drawer link rule and
     the new utilities were the odd ones out at 0.5px and now follow the majority. */
  --dashw:1px;

  /* ---- type scale (30.7.26) ----
     These five were declared identically in index / catalog / projects /
     contact / product / project / about. Hoisted here so there is one source.

     The vw term and the desktop ceiling are unchanged, so nothing moves at
     1440. What changed is the FLOORS: --body used to bottom out at 12px, and
     since 1.389vw only reaches 20px at 1440, every width below ~1080px was
     reading body copy at 12px — that is the "text looks small" on tablet.
       body  12 → 15    sub 15 → 17    meta 12 → 13
     A page that genuinely needs different sizes overrides locally (the catalog
     keeps its own bigger --sub and smaller --name for card captions). */
  --h2:  clamp(24px, 2.78vw,  40px);
  --sub: clamp(18px, 1.667vw, 24px);
  --body:clamp(16px, 1.389vw, 20px);   /* 16 is the floor for running copy (noa) */
  --name:clamp(16px, 1.389vw, 20px);
  --meta:clamp(14px, 1.111vw, 16px);   /* captions/labels stay a step under body */
}

/* ============================================================
   STACK-AT — side-by-side becomes one column
   Noa's list asks for this in seven places (contact sections ×2,
   behind-the-brand, distributors ×2, reference ×2). One utility
   instead of seven bespoke media queries.

     .stack-md  → stacks from tablet down (≤1023)
     .stack-sm  → stacks on phones only (≤767)

   Works on a flex row and on a 2-up grid. Caveat: a page rule that
   explicitly declares flex-direction/grid-template-columns loads
   after frame.css and wins, so those need the class plus a local
   override — which is still one line instead of a media query.
   ============================================================ */
@media (max-width:1023px){
  .stack-md{flex-direction:column;grid-template-columns:minmax(0,1fr)}
}
@media (max-width:767px){
  .stack-sm{flex-direction:column;grid-template-columns:minmax(0,1fr)}
}

.dash-t{background:var(--dash) left top / 100% var(--dashw) no-repeat}
.dash-b{background:var(--dash) left bottom / 100% var(--dashw) no-repeat}
.dash-tb{background:var(--dash) left top / 100% var(--dashw) no-repeat,
                     var(--dash) left bottom / 100% var(--dashw) no-repeat}

/* ============================================================
   THE SNAP FRAME — one definition for the whole site
   Was copy-pasted into index / projects / product / project /
   about (and as .cframe in contact), each on height:100vh.

   Snap holds at EVERY width, mobile included: a section that
   doesn't fit a phone gets re-composed or split into two frames,
   it is never released to free scroll.

   100svh, not 100vh: svh is the *small* viewport height (browser
   chrome visible), so a snapped section never hides under
   Safari's URL bar as it collapses and re-expands on scroll.
   ============================================================ */

/* the viewport (html) is the scroll + snap container — avoids the
   body→viewport overflow-propagation bug that silently breaks snap */
html{scroll-snap-type:y mandatory}

.frame{
  width:100%;height:100svh;
  scroll-snap-align:start;
  display:flex;flex-direction:column;
  padding:var(--nav) var(--m) var(--pb);
  overflow:hidden;background:#fff;
}

/* ---- site-wide interactive transitions ----
   Covers the visual properties used by the site's hover/focus states. More
   specific component transitions (carousels, drawers, moving arrows) keep
   their own timing. */
:where(a[href],button,input,textarea,select,summary,[role="button"],[tabindex]){
  transition-property:color,background-color,border-color,opacity,box-shadow,outline-color,text-decoration-color;
  transition-duration:var(--hover-time);
  transition-timing-function:var(--hover-ease);
}
:where(a[href],button,[role="button"],[tabindex]) :where(svg,svg *){
  transition-property:color,fill,stroke,stroke-width,opacity;
  transition-duration:var(--hover-time);
  transition-timing-function:var(--hover-ease);
}

/* ---- first-arrival section reveals ----
   Page markup opts in with data-reveal. The early frame script arms the
   hidden state before content is parsed; the late pass reveals each target
   once and records it on the current history entry. */
body.es-reveal-ready [data-reveal]:not(.is-revealed):not(.is-reveal-restored){
  opacity:0;
  will-change:opacity,translate,clip-path;
}
body.es-reveal-ready [data-reveal].is-revealed{
  animation:es-reveal-up .72s cubic-bezier(.22,.61,.36,1) both;
  animation-delay:var(--reveal-delay,0ms);
}
body.es-reveal-ready [data-reveal][data-reveal-mode="left"].is-revealed{
  animation-name:es-reveal-left;
}
body.es-reveal-ready [data-reveal][data-reveal-mode="right"].is-revealed{
  animation-name:es-reveal-right;
}
body.es-reveal-ready [data-reveal][data-reveal-mode="clip"].is-revealed{
  animation-name:es-reveal-clip;
  animation-duration:.82s;
  /* backwards, NOT both — this is the bug noa reported as "the dashes are only there if I
     refresh". `both` keeps the animation's last keyframe applied for ever, and that keyframe
     ends at clip-path:inset(0), so the finished reveal left a permanent clip on the block.
     A clip-path cuts at the border box, and two dashes are drawn deliberately 1px OUTSIDE
     their box — .tboxw::before at top:-1px and .ecbox::after at bottom:-1px, both sitting on
     the collage's own edge — so the clip erased exactly those two lines. On a refresh the
     reveal is restored rather than replayed (.is-reveal-restored forces clip-path:none), the
     clip never exists, and the dashes appear: hence "only after a refresh".
     With `backwards` the from-state still applies during the delay, and once the animation
     is done no animated value remains — opacity falls back to 1 and the clip disappears, so
     the arrived state and the refreshed state are identical by construction. */
  animation-fill-mode:backwards;
}
body.es-reveal-ready [data-reveal][data-reveal-text].is-revealed{
  opacity:1;
  animation:none;
}
body.es-reveal-ready [data-reveal-text]:not(.is-revealed):not(.is-reveal-restored) .es-reveal-word{
  opacity:0;
  translate:0 .7em;
}
body.es-reveal-ready [data-reveal-text].is-revealed .es-reveal-word{
  display:inline-block;
  animation:es-reveal-word .52s cubic-bezier(.22,.61,.36,1) both;
  animation-delay:calc(var(--reveal-delay,0ms) + var(--word-index,0) * 14ms);
}
body.es-reveal-ready [data-reveal].is-reveal-restored,
body.es-reveal-ready.es-reveal-fallback [data-reveal]{
  opacity:1 !important;
  translate:none !important;
  clip-path:none !important;
  animation:none !important;
}
@keyframes es-reveal-up{
  from{opacity:0;translate:0 22px}
  to{opacity:1;translate:none}
}
@keyframes es-reveal-left{
  from{opacity:0;translate:-24px 0}
  to{opacity:1;translate:none}
}
@keyframes es-reveal-right{
  from{opacity:0;translate:24px 0}
  to{opacity:1;translate:none}
}
@keyframes es-reveal-clip{
  from{opacity:0;clip-path:inset(0 0 14% 0)}
  to{opacity:1;clip-path:inset(0)}
}
@keyframes es-reveal-word{
  from{opacity:0;translate:0 .7em}
  to{opacity:1;translate:none}
}

/* ---- cross-page navigation ----
   Native same-origin view transitions keep the fixed site chrome visually
   persistent while only the page content changes. Unsupported browsers keep
   normal navigation without affecting layout or interaction. */
@view-transition{navigation:auto}
.topbar{view-transition-name:es-topbar}
.rail-fixed.l{view-transition-name:es-rail-left}
.rail-fixed.r{view-transition-name:es-rail-right}
.bottomline-fixed{view-transition-name:es-bottomline}

::view-transition-group(root){
  animation-duration:.28s;
  animation-timing-function:var(--hover-ease);
}
::view-transition-old(root){animation:es-page-out .18s ease both}
::view-transition-new(root){animation:es-page-in .28s var(--hover-ease) both}
@keyframes es-page-out{to{opacity:0}}
@keyframes es-page-in{from{opacity:0}}

/* Both chrome snapshots are opaque white, so they must NOT plus-lighter
   blend (that washes the 0.5px lines out to nothing mid-transition). */
::view-transition-old(es-topbar),
::view-transition-new(es-topbar),
::view-transition-old(es-rail-left),
::view-transition-new(es-rail-left),
::view-transition-old(es-rail-right),
::view-transition-new(es-rail-right),
::view-transition-old(es-bottomline),
::view-transition-new(es-bottomline){mix-blend-mode:normal}

/* Rails and the bottom line are pure CSS strokes at identical geometry on
   every page: freeze them so nothing animates at all. */
::view-transition-group(es-rail-left),
::view-transition-group(es-rail-right),
::view-transition-group(es-bottomline),
::view-transition-old(es-rail-left),
::view-transition-new(es-rail-left),
::view-transition-old(es-rail-right),
::view-transition-new(es-rail-right),
::view-transition-old(es-bottomline),
::view-transition-new(es-bottomline){animation:none}

/* The navbar is NOT frozen. Its snapshot on the incoming page is captured at
   that document's first render, and the pieces that need a resource — the
   logo image and the webfont text — can still be a frame away even with the
   preloads in every <head>. Freezing that snapshot (animation:none) would
   hold an empty navbar on screen for the whole transition, which reads as a
   page refresh. Instead the outgoing navbar is held on top for 120ms and the
   incoming one fades in over 160ms: by then it is fully painted, and the two
   bits that legitimately differ between pages — the ruler hatch and the page
   name — cross-fade instead of jumping. The group is frozen because the
   navbar box is the same on every page. */
::view-transition-group(es-topbar){animation:none}
::view-transition-old(es-topbar){animation:es-chrome-out .16s linear .12s both}
::view-transition-new(es-topbar){animation:es-chrome-in .16s linear .12s both}
@keyframes es-chrome-out{to{opacity:0}}
@keyframes es-chrome-in{from{opacity:0}}

@media (prefers-reduced-motion:reduce){
  body [data-reveal]{
    opacity:1 !important;
    translate:none !important;
    clip-path:none !important;
    animation:none !important;
  }
  body .es-reveal-word{opacity:1 !important;translate:none !important;animation:none !important}
  ::view-transition-group(root),
  ::view-transition-old(root),
  ::view-transition-new(root),
  ::view-transition-old(es-topbar),
  ::view-transition-new(es-topbar){animation-duration:.01ms;animation-delay:0s}
}

/* ---- arrival guard ----
   frame.js puts .es-chrome-init on <body> in its early (shell) pass and drops it
   once the bottom bar and page name have been settled into the state the current
   scroll position actually implies. Everything the arrival code touches must be
   listed here, or it animates from its CSS resting value to its real one right
   after the page paints — which is the "the frame reloaded" flicker. */
body.es-chrome-init .bottombar,
body.es-chrome-init .bottombar>*,
body.es-chrome-init .nav-right .pagename,
body.es-chrome-init .hatch,
body.es-chrome-init .mnav{transition:none !important}

/* ---- fixed full-height side rails ---- */
.rail-fixed{position:fixed;top:0;bottom:0;width:var(--line);background:var(--frameline);z-index:101;pointer-events:none}
.rail-fixed.l{left:var(--m)}
.rail-fixed.r{right:var(--m)}

/* ---- fixed navbar ---- */
.topbar{position:fixed;top:0;left:0;right:0;height:var(--nav);z-index:100;background:#fff;display:flex;flex-direction:column}
.navrow{flex:1;display:flex;align-items:stretch;min-height:0}
.nav-cell{width:var(--cell);display:flex;align-items:center;flex-shrink:0}
.nav-left{justify-content:center;padding-left:var(--m)}
.nav-right{justify-content:center;padding-right:var(--m)}
.navdiv{position:absolute;top:0;height:var(--nav);width:var(--line);background:var(--frameline);z-index:3;pointer-events:none}
.navdiv.l{left:var(--cell)}
.navdiv.r{right:var(--cell)}
.nav-left .logo{width:clamp(84px,7.5vw,108px);height:auto}
/* nav links (Figma 1:1): 16px font, 16px side padding, 8px gap —
   the link box (text + padding) is also the width of its hatch indicator */
.nav-mid{flex:1;display:flex;align-items:center;justify-content:center;gap:8px}
.nav-mid a{padding:0 16px;font-size:16px;font-weight:500;color:var(--dark);text-decoration:none;white-space:nowrap}
.nav-mid a:hover,
.nav-mid a:focus-visible{color:var(--prime)}
.nav-mid a.active{color:var(--prime)}
.nav-right .pagename{font-size:14px;font-weight:300;color:var(--dark);text-transform:uppercase;white-space:nowrap;letter-spacing:.02em}
body.es-footer-inview .nav-right .pagename,
body.es-footer-inview .hatch-page{opacity:0 !important;visibility:hidden !important}
body[data-hide-page-name-on-first-frame] .nav-right .pagename{
  opacity:1;
  transform:translateY(0);
  transition:opacity .4s ease,transform .4s cubic-bezier(.22,.61,.36,1);
}
body[data-hide-page-name-on-first-frame] .nav-right .pagename.is-hidden{
  opacity:0;
  transform:translateY(-5px);
  pointer-events:none;
}

/* ---- indicator ruler: 8px row bounded by 0.5px lines ---- */
.ruler{height:8px;border-top:var(--line) solid var(--frameline);border-bottom:var(--line) solid var(--frameline);position:relative;flex:none}
/* hatch (Figma 1:1): the indicator is a boxed segment — it has its own
   0.5px frame (side walls bridging the two ruler lines) and is filled with
   0.5px diagonal "/" strokes drawn as an SVG tile, exactly as in Figma:
   45° strokes, 3.657px horizontal step, 5.657px band that stops short of
   the ruler lines (~1.4px inset) */
.hatch{position:absolute;top:calc(-1*var(--line));bottom:calc(-1*var(--line));display:none;pointer-events:none;
       /* no borders at all — every edge of the logo/page segments coincides with
          an existing frame line (ruler lines, side rails, cell dividers), and
          drawing over them snaps to a different device pixel, doubling to 1px.
          The .hatch-nav variant adds its own side walls (no line exists there). */
       border:0 solid var(--prime);
       background-origin:border-box;
       background-repeat:repeat-x;
       background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='3.65686' height='5.65685' viewBox='0 0 3.65686 5.65685'%3E%3Cg stroke='%23FF6638' stroke-width='0.5'%3E%3Cline x1='0' y1='0' x2='-5.65685' y2='5.65685'/%3E%3Cline x1='3.65686' y1='0' x2='-1.99999' y2='5.65685'/%3E%3Cline x1='7.31372' y1='0' x2='1.65687' y2='5.65685'/%3E%3C/g%3E%3C/svg%3E");
       background-size:3.65686px 5.65685px;
       background-position:0 1.4px}
/* nav-link hatch: in Figma this segment's box has 1px side walls and its
   strokes step every 4px (band 6px, 1px inset); its top/bottom box edges
   are clipped by the ruler row, so only the side walls render */
.hatch-nav{border-top:0;border-bottom:0;border-left:1px solid var(--prime);border-right:1px solid var(--prime);
       display:block;opacity:0;visibility:hidden;
       transition:left var(--hover-time) var(--hover-ease),width var(--hover-time) var(--hover-ease),
                  opacity var(--hover-time) var(--hover-ease),visibility 0s linear var(--hover-time);
       background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='6' viewBox='0 0 4 6'%3E%3Cg stroke='%23FF6638' stroke-width='0.5'%3E%3Cline x1='0' y1='0' x2='-6.505' y2='6'/%3E%3Cline x1='4' y1='0' x2='-2.505' y2='6'/%3E%3Cline x1='8' y1='0' x2='1.495' y2='6'/%3E%3C/g%3E%3C/svg%3E");
       background-size:4px 6px;
       background-position:0 1px}
.hatch-nav.on{opacity:1;visibility:visible;transition-delay:0s}
.hatch.on{display:block}
.hatch-logo{left:var(--m);width:calc(var(--cell) - var(--m))}
.hatch-page{right:var(--m);width:calc(var(--cell) - var(--m))}
body[data-hide-page-name-on-first-frame] .hatch-page{
  display:block;
  opacity:0;
  visibility:hidden;
  transition:opacity .4s ease,visibility 0s linear .4s;
}
body[data-hide-page-name-on-first-frame] .hatch-page.on{
  opacity:1;
  visibility:visible;
  transition-delay:0s;
}
/* .hatch-nav is positioned by frame.js under the active nav link */

@media (prefers-reduced-motion:reduce){
  body[data-hide-page-name-on-first-frame] .nav-right .pagename,
  body[data-hide-page-name-on-first-frame] .hatch-page{transition:none}
}

/* ---- fixed bottom chrome: white 40px strip + line pinned to the viewport,
        the page scrolls behind it (like the navbar and the rails) ---- */
.frame{position:relative}
.frame::after{content:none} /* per-frame lines replaced by the fixed one */
.bottomline-fixed{position:fixed;left:0;right:0;bottom:0;height:var(--pb);background:#fff;
                  border-top:var(--line) solid var(--frameline);z-index:99;pointer-events:none}
/* frames with a bottombar clear the fixed 80px bar at snap position */
.frame.has-bbar{padding-bottom:var(--bbh,var(--pbx)) !important}

/* ---- frame bottom edge (expanded): 64px toolbar ----
   in-flow bar at the bottom of a frame; add .fixed for a
   viewport-pinned bar (e.g. a persistent filter bar) */
/* A block's own copy of a frame line disappears while it sits on top of the real rail, so
   the rail is never painted twice (frame.js adds .on-rail — see the railCopies block).
   Both pseudos are listed because different blocks draw the copy on different sides;
   hiding one that does not exist costs nothing.
   `background:none` rather than `opacity:0` on purpose: the copies paint their line as a
   background (a solid fill on the product hero, a repeating gradient on the text blocks),
   and removing the paint is both the direct expression of "do not draw this line" and the
   thing that can actually be verified — a pseudo-element's computed opacity reads back as
   1 in Chrome even when the rule is applying, while its background-color reads honestly. */
[data-railcopy].on-rail::before,
[data-railcopy].on-rail::after{background:none}
/* and the same for a plain border sitting on a vertical rail (see data-railedge in
   frame.js): a divider inside a moving track cannot know in CSS whether it currently
   lands on a rail, so it is told at runtime.
   !important is needed, not laziness: the divider is set with the `border-right` SHORTHAND
   from a `.s3-card:not(:last-child) .cimg` selector, which carries both a higher
   specificity and the colour, so a plain longhand override here lost. A suppression rule
   whose whole job is "never paint on the rail" is the right place for it. */
[data-railedge].edge-on-rail{border-right-color:transparent !important;
                             border-left-color:transparent !important}

/* This border-top is the VISIBLE bottom line and must stay. It looks like a duplicate of
   .bottomline-fixed — same top, same height, same 0.5px stroke — and it was removed on that
   basis, which deleted the line from the whole site. The reason: .bottombar is z-index 100
   with an opaque white fill and .bottomline-fixed is z-index 99, so the bar paints straight
   over the fixed rail's border. Two strokes are declared; only ever one is seen.
   The lesson: count what is PAINTED, not what is declared. A CSS-only audit called this a
   doubling; noa saw the line disappear. */
.bottombar{flex:none;height:var(--pbx);background:#fff;border-top:var(--line) solid var(--frameline);
           display:flex;align-items:center;justify-content:center;
           position:fixed;left:0;right:0;bottom:0;z-index:100;overflow:hidden;
           transition:height .25s ease, opacity .3s ease} /* eases the scroll-driven 40↔80 + symmetric bar crossfade */
/* scroll-linked: frame.js drives the height (40↔80) so the content itself pushes
   the line up / pulls it down; the bar content fades with the same progress */
.bottombar>*{opacity:var(--bopacity,1);transition:opacity .3s ease}
.bottombar.fixed{position:fixed;left:0;right:0;bottom:0;z-index:100}

/* ============================================================
   RESPONSIVE FRAME
   Breakpoints (site-wide): ≤767px mobile · 768–1023px tablet · ≥1024px desktop
   Mobile: tighter inset, nav links collapse into a burger + drawer.
   ============================================================ */
.nav-burger{display:none}
.mnav{display:none}

@media (max-width:1023px){
  :root{--m:24px;--pbx:72px}
  .nav-right .pagename{font-size:12px}
}

@media (max-width:767px){
  /* the bottom strip and the raised bar both lose height on a phone — that
     height comes straight out of --fh, the budget a section has to fit in.
     frame.js reads these tokens, so the scroll-linked bar follows too. */
  :root{--m:16px;--nav:56px;--pb:28px;--pbx:60px}

  /* navrow: logo | (page name) | burger — the desktop cells dissolve */
  .nav-cell{width:auto}
  .nav-left{padding-left:calc(var(--m) + 12px)}
  .nav-mid{display:none}
  /* Mobile navbar (noa, 30.7.26): two bounded cells, the page name back in.
       | logo |  ...........  | name  burger |
     The desktop dividers are positioned at fixed --cell offsets (17.6% in from each
     edge), meaningless once the cells go auto-width, so they are dropped and each
     cell closes itself with its own line.
     Logo cell: equal padding either side of the logo rather than a fixed width. The
     cell inherits justify-content:center, and with an asymmetric padding that centred
     the logo inside it — measured 17.5px before the logo and 6px after. flex-start
     plus 12px of padding on each side (the leading one on top of --m) makes the two
     gaps identical and lets the cell width follow the logo at any breakpoint.
     Right cell: the line closes it on the leading edge, the page name sits inside it
     next to the burger with the matching 12px inset. */
  .navdiv{display:none}
  /* --navpad is the one inset every cell breathes by (noa asked for more room on all
     of them, 30.7.26): the logo either side of itself, the page name either side of
     itself, and the burger glyph after the name's closing line. It lives on :root so
     the ruler's hatches — which are siblings of .navrow, not children — can size
     themselves from the same number. */
  :root{--navpad:16px}
  .nav-left{position:relative;width:auto;justify-content:flex-start;
    padding-left:calc(var(--m) + var(--navpad));padding-right:var(--navpad)}
  /* TWO separators (noa, 30.7.26): one closing the logo, one opening the burger, with
     the page name sitting between them. Both run the FULL navbar height, through the
     8px ruler row as well, the way the desktop .navdiv does — the pseudo-elements live
     inside .navrow, so they are extended past its bottom edge by the ruler's height. */
  .nav-left::after{content:"";position:absolute;right:0;top:0;bottom:-8px;
    width:var(--line);background:var(--frameline);pointer-events:none;z-index:4}
  /* the name is centred in the space between the two separators, not pinned to one
     end of it (noa, 30.7.26) */
  .nav-right{display:flex;position:relative;flex:1;min-width:0;
    padding-inline:var(--navpad);justify-content:center;align-items:center}
  .nav-right::after{content:"";position:absolute;right:0;top:0;bottom:-8px;
    width:var(--line);background:var(--frameline);pointer-events:none;z-index:4}
  .nav-right::before{display:none}
  /* …and the name has to be able to give way. It is `nowrap` and centred with no bound,
     so a long one spilled out of its cell in BOTH directions: at 320 "ATID EDUCATION
     NETWORK" measured 173px and ran from x=109 — inside the logo cell, which ends at 144
     — to 283, riding over the logo and the burger. It fits exactly at 390, which is where
     this navbar was measured, so nothing changes there; below that it truncates. */
  /* …and the name WRAPS rather than truncates. Measured need vs. have at 12px: "ATID
     EDUCATION NETWORK" wants 173px against 142 available at 390, 112 at 360 and 72 at 320,
     so three names were cut at 390 and five at 320. Shrinking the cell's inset only buys
     32px — enough at 390, nowhere near it at 320 — and a font small enough to fit 173px into
     72px would be unreadable. Two lines of 12px are 28px inside a 48px navbar, so the name
     can simply take a second line and stay whole; `balance` splits it evenly instead of
     leaving one word alone. The inset is halved as well, which keeps most names on one line. */
  /* ONE LINE with an ellipsis (noa: "שלוש נקודות ושיהיה מה שנכנס בשורה"). Wrapping to two
     lines was tried and rejected — she prefers the name clipped on a single line. What stays
     from that attempt is the extra WIDTH, which is what decides how much of the name you
     actually read before the dots: the cell's insets are trimmed below, so more characters
     fit than before. `nowrap` comes from the base rule. */
  .nav-right .pagename{font-size:12px;min-width:0;overflow:hidden;text-overflow:ellipsis}
  .nav-right{padding-right:calc(var(--m) / 2)}
  /* Below 360 the cell is only 104px wide, and at 12px the longest names needed a THIRD
     line — 41px of text inside a 48px navbar, crammed against both nav rules. One point
     smaller is enough to bring every name back to two lines. */
  @media (max-width:360px){
    /* Below 360 the cell is 104px and 24px of it was padding (16 left + 8 right), leaving
       only 80 to read the name in. 4px each side plus one point smaller buys 22px of extra
       characters before the ellipsis. The separators are drawn on the cell's edges, so 4px
       still reads as an inset rather than text touching a line. */
    .nav-right{padding-left:4px;padding-right:4px}
    .nav-right .pagename{font-size:11px}
  }
  .nav-left .logo{width:96px}
  .hatch-nav{display:none !important}
  /* the hatch runs exactly to the logo's separator — the cell is --navpad on each
     side of a 96px logo, so that is what it measures from the rail. Derived from the
     tokens rather than hard-coded, so it follows if either changes. */
  .hatch-logo{left:var(--m);width:calc(var(--navpad) * 2 + 96px)}
  /* with the menu open the indicator belongs under the X: the burger cell runs from
     the second separator to the right rail. */
  body.mnav-open .hatch-logo{display:none}
  body.mnav-open .hatch-page{display:block !important;left:auto;right:var(--m);
    width:calc(var(--navpad) - 9px + 44px + 5px)}
  /* the page-name hatch goes with the page name itself */
  .hatch-page{display:none !important}

  /* burger — blueprint line + echo dot, stroke 1.5 */
  .nav-burger{display:inline-flex;align-items:center;justify-content:center;
    /* Both icons read at the logo's inset from the rail (noa, 30.7.26). Measured
       against INK, not boxes, and per icon — the two glyphs are not the same width:
       the burger's lines end at x=21 of its 24 viewBox, the X's corners at 19.75, so
       one shared margin can only align one of them. The margin is set for the
       burger (the default state) and .ic-close is nudged by the 2-unit difference.
       Logo ink sits 11.5px from the left rail; both icons now match on the right. */
    /* centred on the LOGO's middle (noa, 31.7.26). The navrow is align-items:stretch, and
       a 44px-tall button in a 48px row therefore sat at flex-start — its ink centre landed
       at y=22 against the logo's 24, the same 2px high at every width. align-self does what
       stretch cannot for a fixed-height item. The glyph's three lines are symmetric about
       y=12 of the 24 viewBox, so centring the button centres the ink. */
    flex:none;align-self:center;width:44px;height:44px;
    /* the 26px glyph is centred in a 44px button, so 9px of the inset is already
       inside it — the margins carry the rest */
    margin-left:calc(var(--navpad) - 9px);margin-right:calc(var(--m) + 5px);
    padding:0;border:0;background:none;color:var(--prime);cursor:pointer}
  .nav-burger svg{width:26px;height:26px;display:block}
  /* 2 viewBox units narrower than the burger, scaled 26/24 = 2.17px */
  .nav-burger .ic-close{display:none;transform:translateX(2.17px)}
  body.mnav-open .nav-burger .ic-menu{display:none}
  body.mnav-open .nav-burger .ic-close{display:block}
  body.es-footer-inview .nav-burger{color:#efeae5}

  /* drawer — slides down from under the navbar, between the fixed rails,
     leaves the 40px bottom strip + line visible */
  /* sides sit on the rails so each link's dash runs the full width of the frame;
     the words keep their inset via the anchor's own padding (noa, 30.7.26) */
  .mnav{display:flex;flex-direction:column;justify-content:flex-start;
    position:fixed;left:0;right:0;top:var(--nav);bottom:var(--pb);z-index:100;
    background:#fff;padding:40px var(--m) 32px;
    transform:translateY(-104%);visibility:hidden;
    transition:transform .35s cubic-bezier(.4,0,.2,1),visibility 0s linear .35s}
  body.mnav-open .mnav{transform:none;visibility:visible;transition-delay:0s}
  body.mnav-open{overflow:hidden}

  .mnav-links{display:flex;flex-direction:column}
  .mnav-links a{font-family:'Welcome web',sans-serif;
    font-size:clamp(28px,8.5vw,36px);font-weight:500;line-height:1;
    color:var(--dark);text-decoration:none;padding:22px 12px}
  /* The divider is a RULER BAND, not a dash: two solid lines with room between them for the
     hatch to live in — the same construction as the desktop `.ruler` under the navbar
     (8px tall, a --line stroke top and bottom). Every row carries the empty band; only the
     current page fills it. Replaces the single dashed line that was here (noa). */
  .mnav-links a::before{content:"";position:absolute;left:0;right:0;bottom:0;height:8px;
    pointer-events:none;
    border-top:var(--line) solid var(--frameline);
    border-bottom:var(--line) solid var(--frameline)}
  /* CURRENT PAGE = the desktop navbar's hatched segment, moved inside the row (noa).
     On desktop the active link is marked by a hatched band in the ruler row; here each
     menu item IS a row, so its own hatched band sits inside it, directly above the row's
     dashed divider. Same 4x6 diagonal tile and the same 1px side walls as .hatch-nav, so
     the marker reads as one language across both navs rather than a second invention. */
  .mnav-links a{position:relative}
  .mnav-links a.active{color:var(--prime)}
  .mnav-links a.active::after{
    /* sits INSIDE the band, between its two lines — same relationship the desktop hatch has
       to the ruler it lives in */
    content:"";position:absolute;left:0;right:0;bottom:var(--line);height:calc(8px - var(--line) * 2);
    pointer-events:none;
    border-left:1px solid var(--prime);border-right:1px solid var(--prime);
    background-origin:border-box;background-repeat:repeat-x;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='6' viewBox='0 0 4 6'%3E%3Cg stroke='%23FF6638' stroke-width='0.5'%3E%3Cline x1='0' y1='0' x2='-6.505' y2='6'/%3E%3Cline x1='4' y1='0' x2='-2.505' y2='6'/%3E%3Cline x1='8' y1='0' x2='1.495' y2='6'/%3E%3C/g%3E%3C/svg%3E");
    background-size:4px 6px;background-position:0 0}
  .mnav-links a:hover,
  .mnav-links a:focus-visible{color:var(--prime)}

  /* .mnav-foot (the mailto line) is no longer injected — see frame.js */
}
