/* ==================================================================
   DESIGN TOKENS
   Every color, font, and the faint background grid come from these
   variables. Change a value here and it updates everywhere it's used.
   ================================================================== */
:root{
  --ink:        #0B1E33;   /* deep blueprint navy — page background */
  --ink-2:      #0F2740;   /* slightly lighter navy — card backgrounds */
  --line:       #5EC8D8;   /* blueprint cyan — links, borders, accents */
  --cyan:       var(--line); /* alias — the video player rules read "cyan" */
  --paper:      #EDEFF2;   /* paper white — primary text */
  --slate:      #86A0B8;   /* muted blue-grey — secondary/supporting text */
  --amber:      #E8A33D;   /* warm accent — used sparingly (e.g. "REV A" tag) */
  --status-open:   #5FD68A;  /* "accepting projects" — green */
  --status-closed: #E8615F;  /* "not accepting" — red */
  --grid-line: rgba(94, 200, 216, 0.4); /* dividers: section rules, titleblock, stack list */
  --grid-line-bg: rgba(94, 200, 216, 0.18); /* the faint drafting-table background grid specifically */
  --font-display: 'Space Grotesk', sans-serif; /* headings */
  --font-body: 'Inter', sans-serif;            /* paragraphs */
  --font-mono: 'JetBrains Mono', monospace;    /* labels, nav, buttons */
  --nav-height: 112px; /* fallback before script.js measures the real bar height */
}

/* Basic reset so every browser starts from the same blank slate. */
*{ margin:0; padding:0; box-sizing:border-box; }

/* Elements intentionally travel off-screen during the About section's
   scroll-scrubbed reveal (see initAboutSideReveal in script.js) — this
   clips that overflow instead of letting it grow the page width and
   trigger a horizontal scrollbar. */
html, body{ overflow-x:hidden; }

/* Reserves the scrollbar's width permanently, whether or not the page
   actually needs to scroll. Without this, any moment the real scrollbar
   would appear or disappear — locking scroll for an open modal, or
   simply navigating from a page tall enough to scroll (like work.html)
   to one that isn't — leaves the viewport briefly wider and everything
   visibly shifts sideways. `scrollbar-gutter` only has an effect on an
   element whose overflow isn't the default `visible`, so it needs the
   explicit `overflow-y:auto` right alongside it — without that second
   line this rule silently does nothing, which was the actual bug. */
html{ overflow-y:auto; scrollbar-gutter:stable; }
html{ scroll-behavior:smooth; } /* smooth jumps for the #work / #about anchor links */

/* Page background: solid navy plus a faint drafting-table grid,
   drawn with two overlapping gradients rather than an image file. */
body{
  background:var(--ink);
  color:var(--paper);
  font-family:var(--font-body);
  line-height:1.5;
  background-image:
    linear-gradient(var(--grid-line-bg) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid-line-bg) 1px, transparent 1px);
  background-size:40px 40px;
}

::selection{ background:var(--line); color:var(--ink); } /* highlighted text color */

a{ color:inherit; text-decoration:none; }

/* Shared content container: caps line length and centers everything.
   Reused by the header, hero, each section, and the footer. */
.wrap{ max-width:1180px; margin:0 auto; padding:0 32px; }

/* ---------- scroll progress bar --------------------------------------
   A thin readout of how far down the page the visitor is, filled by
   initScrollProgress in script.js. It's a direct 1:1 reflection of the
   user's own scroll position (not an ambient/autoplaying effect), so
   it stays active even under prefers-reduced-motion. */
.scroll-progress{
  position:fixed; top:0; left:0;
  width:100%; height:3px;
  background:linear-gradient(90deg, var(--line), var(--amber));
  transform:scaleX(0); transform-origin:left center;
  z-index:80; pointer-events:none;
}

/* ---------- scroll reveal ---------------------------------------------
   Elements marked .reveal fade into place the first time they enter
   the viewport (see observeReveals in script.js). .reveal alone is the
   JS hook and the default fade-up motion; add one modifier class for a
   different entrance style on top of it:
     .reveal-scale  → scales up from 92% (used for cards)
     .reveal-left   → slides in from the left
     .reveal-right  → slides in from the right (pairs with -left for
                      two columns converging toward the center)
     .reveal-blur   → soft blur-to-sharp, no movement (calmer, used for
                      the footer and the glass bundle panel)
   Scoped to .js on <html> so a visitor without JavaScript — who would
   never get "is-visible" added — simply sees everything already in
   place instead of permanently hidden content. --reveal-delay lets a
   group of siblings (project cards, pricing cards) stagger in one
   after another instead of popping in all at once. */
.js .reveal{
  opacity:0; transform:translateY(28px);
  transition:opacity .85s cubic-bezier(.16,1,.3,1), transform .85s cubic-bezier(.16,1,.3,1), filter .85s ease;
  transition-delay:var(--reveal-delay, 0ms);
}
.js .reveal.is-visible{ opacity:1; transform:none; filter:none; }

.js .reveal-scale{ transform:scale(.92); }
.js .reveal-scale.is-visible{ transform:scale(1); }

.js .reveal-left{ transform:translateX(-44px); }
.js .reveal-left.is-visible{ transform:translateX(0); }

.js .reveal-right{ transform:translateX(44px); }
.js .reveal-right.is-visible{ transform:translateX(0); }

.js .reveal-blur{ transform:translateY(14px); filter:blur(10px); }
.js .reveal-blur.is-visible{ filter:blur(0); }

/* ---------- nav : full-bleed bar → "liquid glass" floating pill -------
   Loads flush against the viewport edges, like a ruled title strip on a
   drawing sheet. Past SCROLL_THRESHOLD (script.js) the header gains
   .is-scrolled, which insets it from the top and lets the bar itself
   shrink into the rounded glass pill. Every property that differs
   between the two states is transitioned, so the change reads as one
   continuous move rather than a swap. */
header{
  position:fixed; top:0; left:0; right:0; z-index:50;
  padding:0;
  transition:padding .6s cubic-bezier(.16,1,.3,1);
}
header.is-scrolled{
  padding:20px 32px 0;
}

header nav{
  display:flex; align-items:center; justify-content:space-between; gap:24px;
  flex-wrap:wrap;

  /* full-bleed state: edge to edge, square corners, roomier padding —
     reads as a fixed instrument bar rather than a floating object.
     Background is a good deal darker/more opaque than the page itself
     (not just a faint tint of the same navy), and the bottom edge is a
     bright cyan hairline the full width of the viewport, so the bar is
     unmistakable even before you scroll. */
  width:100%; max-width:100%; margin:0 auto;
  padding:26px 40px;
  border-radius:0;
  background:rgba(6, 16, 28, 0.92);
  -webkit-backdrop-filter:blur(18px) saturate(160%);
  backdrop-filter:blur(18px) saturate(160%);
  border:1px solid transparent;
  border-bottom:1.5px solid rgba(94, 200, 216, 0.4);
  box-shadow:0 12px 28px rgba(0,0,0,0.28);

  transition:
    max-width .6s cubic-bezier(.16,1,.3,1),
    padding .6s cubic-bezier(.16,1,.3,1),
    border-radius .6s cubic-bezier(.16,1,.3,1),
    background .5s ease,
    border-color .5s ease,
    box-shadow .5s ease;
}
header.is-scrolled nav{
  /* scrolled state: the original detached glass pill */
  max-width:1180px;
  padding:16px 28px;
  border-radius:22px;

  background:rgba(15, 39, 64, 0.32);
  -webkit-backdrop-filter:blur(22px) saturate(180%);
  backdrop-filter:blur(22px) saturate(180%);

  border:1px solid rgba(255,255,255,0.10);
  border-top-color:rgba(255,255,255,0.28);
  box-shadow:
    0 10px 34px rgba(0,0,0,0.32),
    inset 0 1px 0 rgba(255,255,255,0.18),
    inset 0 -1px 0 rgba(0,0,0,0.16);
}
.logo{
  font-family:var(--font-mono); font-size:15px; letter-spacing:.06em;
  color:var(--paper); display:flex; align-items:center; gap:10px;
  transition:opacity .2s ease;
}
.logo:hover{ opacity:.8; }
/* The Veidum mark: a slim two-tone folded plane with an amber seam
   (concept "M v2" from the logo exploration). Inline SVG in the HTML so
   it needs no extra request; this just sizes it against the wordmark. */
.logo .logo-mark{ width:26px; height:26px; flex-shrink:0; }

/* Hamburger toggle — invisible on desktop, where .nav-menu is always
   open. Only appears past the mobile breakpoint below. */
.nav-toggle{ display:none; background:none; border:none; padding:0; cursor:pointer; }

/* On desktop this is just a flex row holding the links + language
   switcher (replicates the spacing nav itself used to provide directly
   on its 3 children, back when links/lang-switch were direct siblings
   of the logo instead of wrapped in this menu). On mobile it becomes
   the collapsible dropdown — see the media query below. */
.nav-menu{ display:flex; align-items:center; gap:24px; }

nav .links{ display:flex; gap:32px; font-family:var(--font-mono); font-size:14px; }
nav .links a{ color:var(--slate); transition:color .2s; }
nav .links a:hover{ color:var(--line); }
nav .links a::before{ content:'// '; color:var(--line); opacity:.6; }
nav .links a[aria-current="page"]{ color:var(--line); }

/* Language switcher: small pill buttons inside the same glass bar.
   The active language gets a bright fill; the others stay ghostly
   until hovered, so the group reads as one control, not three links. */
.lang-switch{
  display:flex; gap:4px; padding:4px;
  background:rgba(11,30,51,0.35);
  border:1px solid rgba(255,255,255,0.08);
  border-radius:999px;
}
.lang-btn{
  font-family:var(--font-mono); font-size:12px; letter-spacing:.04em;
  color:var(--slate); background:transparent; border:none;
  padding:7px 14px; border-radius:999px; cursor:pointer;
  transition:background .2s ease, color .2s ease;
}
.lang-btn:hover{ color:var(--paper); }
.lang-btn.is-active{ background:var(--line); color:var(--ink); font-weight:700; }

/* ---------- mobile nav : hamburger + dropdown -------------------------
   Below this width the links + language switcher no longer fit
   comfortably in one row, so they collapse behind a hamburger button
   into a glass dropdown instead of wrapping onto an ugly, tall second
   row. Toggled by initMobileNav in script.js (adds/removes .is-open
   and keeps aria-expanded in sync). */
@media (max-width:700px){
  header nav{ padding:16px 20px; position:relative; }
  header.is-scrolled nav{ padding:12px 18px; }

  .nav-toggle{
    display:flex; flex-direction:column; justify-content:center; align-items:center; gap:5px;
    width:44px; height:44px; /* 44px min touch target (icon itself stays visually 26px) */
  }
  .nav-toggle span{
    display:block; width:26px; height:2px; border-radius:1px;
    background:var(--paper);
    transition:transform .25s ease, opacity .25s ease;
  }
  /* the classic hamburger → X morph when open */
  .nav-toggle[aria-expanded="true"] span:nth-child(1){ transform:translateY(7px) rotate(45deg); }
  .nav-toggle[aria-expanded="true"] span:nth-child(2){ opacity:0; }
  .nav-toggle[aria-expanded="true"] span:nth-child(3){ transform:translateY(-7px) rotate(-45deg); }

  .nav-menu{
    position:absolute; top:calc(100% + 12px); left:20px; right:20px;
    flex-direction:column; align-items:stretch; gap:20px;
    padding:22px 24px;
    border-radius:18px;
    background:rgba(8, 20, 35, 0.97);
    -webkit-backdrop-filter:blur(30px) saturate(180%);
    backdrop-filter:blur(30px) saturate(180%);
    border:1px solid rgba(255,255,255,0.10);
    border-top-color:rgba(255,255,255,0.24);
    box-shadow:0 20px 45px rgba(0,0,0,0.45), inset 0 1px 0 rgba(255,255,255,0.12);
    opacity:0; visibility:hidden; transform:translateY(-10px);
    pointer-events:none;
    transition:opacity .25s ease, transform .25s ease, visibility .25s;
  }
  .nav-menu.is-open{
    opacity:1; visibility:visible; transform:translateY(0); pointer-events:auto;
  }
  .nav-menu .links{ flex-direction:column; gap:18px; font-size:15px; }
  .nav-menu .lang-switch{ align-self:flex-start; }
}

/* ---------- hero : intro + title-block stamp ------------------------
   The big headline area, finished with a 4-column strip styled like
   the info stamp in the corner of a real engineering drawing. */
/* top clearance = the fixed nav's real measured height (set as
   --nav-height by script.js) plus a fixed breathing gap, so the hero
   never sits under the bar on any screen size, wrapped nav included */
.hero{
  padding-top:calc(var(--nav-height) + 32px);
  padding-bottom:clamp(40px, 6vw, 64px);
  position:relative;
}

/* Two columns: sketch/copy on the left, the "finished result" preview
   on the right. Stacks on narrow screens (visual moves below the text). */
.hero-grid{
  display:grid; grid-template-columns:1.05fr 0.95fr; gap:56px; align-items:center;
}
/* A glass panel behind the hero copy — lifts it off the faint drafting
   grid running behind everything, the same visual language as the nav,
   just more subtle since it's sitting behind body text, not floating
   over the page. */
.hero-content{
  min-width:0;
  background:rgba(15, 39, 64, 0.62);
  -webkit-backdrop-filter:blur(16px) saturate(160%);
  backdrop-filter:blur(16px) saturate(160%);
  border:1.5px solid rgba(94, 200, 216, 0.35);
  border-top:1.5px solid rgba(255,255,255,0.26);
  border-radius:20px;
  padding:clamp(20px, 3vw, 32px);
  box-shadow:
    0 14px 30px rgba(0,0,0,0.32),
    inset 0 1px 0 rgba(255,255,255,0.12);
}

@media (max-width:960px){
  .hero-grid{ grid-template-columns:1fr; gap:52px; }
}

.eyebrow{
  font-family:var(--font-mono); font-size:13px; color:var(--line);
  letter-spacing:.12em; text-transform:uppercase; margin-bottom:16px;
  display:flex; align-items:center; gap:10px;
}
.eyebrow .rule{ width:36px; height:2px; background:var(--line); }

/* The two-column layout caps this column at roughly 560px regardless
   of how wide the browser window is (the .wrap around it maxes out at
   1180px total) — so the old 76px max size wrapped onto 4-5 lines even
   on a wide desktop monitor, not just at "in-between" widths. Capping
   it lower fixes that at every viewport width in one place. */
h1{
  font-family:var(--font-display); font-weight:600;
  font-size:clamp(30px, 3.6vw, 48px); line-height:1.08; letter-spacing:-.01em;
  max-width:820px;
}
h1 span{ color:var(--line); }

.hero p.lede{
  font-family:var(--font-body); font-size:18px; color:var(--slate);
  max-width:520px; margin-top:18px;
}

.hero-actions{ display:flex; gap:16px; margin-top:28px; flex-wrap:wrap; }

/* On small phones the two CTAs stack full-width — one comfortable
   thumb target per row beats two narrow side-by-side buttons. */
@media (max-width:480px){
  .hero-actions{ flex-direction:column; gap:12px; }
  .hero-actions .btn{ width:100%; justify-content:center; }
}
.btn{
  font-family:var(--font-mono); font-size:13px; padding:13px 24px;
  border:1px solid var(--line); color:var(--paper);
  display:inline-flex; align-items:center; gap:8px; transition:all .2s;
}
.btn.primary{ background:var(--line); color:var(--ink); font-weight:700; }
.btn.primary:hover{ background:var(--paper); border-color:var(--paper); }
.btn.ghost:hover{ background:var(--grid-line); }
/* Shared lift-and-glow on top of whichever background/border change
   above already applies — kept as one rule so every .btn variant
   (including ones added later) gets the same hover weight for free. */
.btn:hover{ transform:translateY(-2px); box-shadow:0 10px 24px -8px rgba(94,200,216,0.45); }
.btn:active{ transform:translateY(0); box-shadow:none; }

/* the title-block strip near the bottom of the hero — like the
   bottom-corner stamp on a real drawing. Two rows (Web/UX line, SMM
   line) x 3 columns (Specialization, Status, Location); Location
   doesn't change per service line, so it spans both rows instead of
   repeating — grid-row:span 2 on .tb-span2, DOM order left-to-right
   top-to-bottom (spec-web, status-web, location, spec-smm, status-smm)
   lets CSS grid auto-placement slot everything in correctly. */
.titleblock{
  margin-top:48px; border:1.5px solid rgba(94, 200, 216, 0.35); border-top:1.5px solid rgba(255,255,255,0.26);
  display:grid; grid-template-columns:repeat(3, 1fr); font-family:var(--font-mono);
  border-radius:16px; overflow:hidden;
  background:rgba(15, 39, 64, 0.62);
  -webkit-backdrop-filter:blur(16px) saturate(160%);
  backdrop-filter:blur(16px) saturate(160%);
  box-shadow:
    0 14px 30px rgba(0,0,0,0.32),
    inset 0 1px 0 rgba(255,255,255,0.12);
}
.tb-cell{ padding:16px 20px; }
.tb-col-1, .tb-col-2{ border-right:1.5px solid rgba(255,255,255,0.10); }
.tb-row-1{ border-bottom:1.5px solid rgba(255,255,255,0.10); }
.tb-span2{ grid-row:span 2; display:flex; flex-direction:column; justify-content:center; }
.titleblock .tb-label{ font-size:10px; color:var(--slate); letter-spacing:.1em; text-transform:uppercase; margin-bottom:6px; }
.titleblock .tb-value{ font-size:14px; color:var(--paper); }

/* Status field: a small glowing dot + colored text, like an availability
   indicator. Toggle STUDIO_ACCEPTING_PROJECTS / STUDIO_ACCEPTING_SMM in
   script.js to flip either row independently — text, dot color, and
   glow all update together. */
.status-value{ display:flex; align-items:center; gap:8px; }
.status-dot{
  width:8px; height:8px; border-radius:50%; flex-shrink:0;
  background:var(--status-open);
  box-shadow:0 0 8px var(--status-open);
}
.status-value.is-open{ color:var(--status-open); }
.status-value.is-open .status-dot{ background:var(--status-open); box-shadow:0 0 8px var(--status-open); }
.status-value.is-closed{ color:var(--status-closed); }
.status-value.is-closed .status-dot{ background:var(--status-closed); box-shadow:0 0 8px var(--status-closed); }

/* Mobile: the desktop's 3-col/2-row grid (with Location spanning both
   rows) doesn't translate to a narrow screen as five plain stacked
   rows — that just reads as a boring form. Instead, group each
   service's spec+status side by side, with the shared Location as its
   own highlighted row between them (grid-template-areas re-places the
   five cells regardless of their DOM/desktop column-row classes). */
@media (max-width:700px){
  .titleblock{
    grid-template-columns:1fr 1fr;
    grid-template-areas:
      "specweb   statusweb"
      "location  location"
      "specsmm   statussmm";
    border-radius:14px; overflow:hidden;
  }
  .tb-cell{ padding:14px 16px; }
  .tb-col-1, .tb-col-2{ border-right:none; }
  .tb-row-1{ border-bottom:none; }

  .tb-col-1.tb-row-1{ grid-area:specweb; border-right:1.5px solid rgba(255,255,255,0.10); border-bottom:1.5px solid rgba(255,255,255,0.10); }
  .tb-col-2.tb-row-1{ grid-area:statusweb; border-bottom:1.5px solid rgba(255,255,255,0.10); }
  .tb-col-3.tb-span2{
    grid-area:location; grid-row:auto;
    border-bottom:1.5px solid rgba(255,255,255,0.10);
    background:rgba(94, 200, 216, 0.06); /* faint highlight — signals "shared", not repeated per row */
    align-items:center; text-align:center;
  }
  .tb-col-1.tb-row-2{ grid-area:specsmm; border-right:1.5px solid rgba(255,255,255,0.10); }
  .tb-col-2.tb-row-2{ grid-area:statussmm; }
}

/* ==================================================================
   HERO VISUAL : the "hook"
   A tilted 3D preview of a finished landing page — as if the blueprint
   on the left had just been built. --tiltx/--tilty are the resting
   rotation values; script.js nudges them toward the cursor on devices
   with a mouse (see initHeroParallax). Everything else (the floating
   stat card, the cursor badge) is positioned relative to this same
   3D scene via translateZ, so they read as sitting in front of the
   browser card rather than pasted on top of it.
   ================================================================== */
.hero-visual{
  perspective:1600px;
  display:flex; align-items:center; justify-content:center;
}
.hero-visual-inner{
  position:relative;
  width:100%; max-width:440px;
  transform-style:preserve-3d;
  --tiltx:8deg; --tilty:-14deg;
  transform:rotateX(var(--tiltx)) rotateY(var(--tilty));
  transition:transform .5s cubic-bezier(.16,1,.3,1);
  animation:hero-visual-in .9s cubic-bezier(.16,1,.3,1) both;
  animation-delay:.15s;
}

@keyframes hero-visual-in{
  from{ opacity:0; transform:rotateX(calc(var(--tiltx) + 6deg)) rotateY(calc(var(--tilty) - 10deg)) translateY(24px) scale(.94); }
  to{ opacity:1; transform:rotateX(var(--tiltx)) rotateY(var(--tilty)) translateY(0) scale(1); }
}

/* The browser card itself — the "finished" landing page */
.browser-card{
  position:relative;
  border-radius:16px;
  background:linear-gradient(165deg, rgba(18,44,70,0.97), rgba(9,24,42,0.99));
  border:1px solid rgba(94,200,216,0.3);
  box-shadow:
    0 40px 70px -20px rgba(0,0,0,0.55),
    0 14px 30px rgba(0,0,0,0.35);
  overflow:hidden;
  transform:translateZ(0);
}
.browser-chrome{
  display:flex; align-items:center; gap:8px;
  padding:14px 16px;
  background:rgba(5,14,26,0.65);
  border-bottom:1px solid rgba(94,200,216,0.18);
}
.chrome-dot{ width:9px; height:9px; border-radius:50%; }
.chrome-dot.dot-r{ background:#E8617A; }
.chrome-dot.dot-y{ background:var(--amber); }
.chrome-dot.dot-g{ background:#6FCF97; }
.browser-url{
  margin-left:8px; flex:1;
  font-family:var(--font-mono); font-size:11px; color:var(--slate);
  background:rgba(255,255,255,0.04); border-radius:6px; padding:5px 12px;
}

.browser-body{ padding-bottom:20px; }
.bb-nav{ display:flex; gap:14px; padding:14px 20px 0; }
.bb-nav span{ width:26px; height:5px; border-radius:3px; background:rgba(237,239,242,0.18); }
.bb-nav span:first-child{ width:34px; background:rgba(94,200,216,0.5); }

/* The hero "photo": a rich gradient instead of a real image, so the
   mockup needs no external assets and never breaks. A slow diagonal
   shine sweeps across it once every few seconds — the one bit of
   ambient motion, kept subtle and slow so it reads as gloss, not noise. */
.bb-image{
  position:relative; margin:16px 20px 0; height:170px;
  border-radius:12px; overflow:hidden;
  background:linear-gradient(135deg, #6ED4E3 0%, #2E6B86 45%, #0B1E33 100%);
}
.bb-image::after{
  content:''; position:absolute; inset:0;
  background:linear-gradient(115deg, transparent 40%, rgba(255,255,255,0.16) 50%, transparent 60%);
  transform:translateX(-100%);
  animation:shine-sweep 4.5s ease-in-out infinite;
  animation-delay:1.6s;
}
@keyframes shine-sweep{
  0%, 30%{ transform:translateX(-120%); }
  55%, 100%{ transform:translateX(120%); }
}
.bb-overlay-cta{
  position:absolute; left:14px; bottom:14px;
  display:flex; align-items:center; gap:7px;
  background:rgba(11,30,51,0.55);
  -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
  border:1px solid rgba(255,255,255,0.25);
  color:var(--paper); font-family:var(--font-mono); font-size:11px;
  padding:8px 14px; border-radius:8px;
}
.cta-dot{ width:6px; height:6px; border-radius:50%; background:var(--amber); box-shadow:0 0 6px var(--amber); }

.bb-text-lines{ padding:18px 20px 0; }
.bb-line{ height:8px; border-radius:4px; background:rgba(237,239,242,0.14); margin-bottom:9px; }
.bb-line.w1{ width:72%; }
.bb-line.w2{ width:46%; }
.bb-buttons{ display:flex; gap:10px; padding:14px 20px 0; }
.bb-btn-solid{ width:112px; height:30px; border-radius:7px; background:var(--line); }
.bb-btn-ghost{ width:88px; height:30px; border-radius:7px; border:1px solid rgba(94,200,216,0.4); }

/* Floating cards: sit "in front of" the browser card in 3D space via
   translateZ, each with its own gentle bob so the scene feels alive
   without anything spinning or sliding distractingly. */
.float-card{
  position:absolute;
  background:rgba(15,39,64,0.88);
  -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
  border:1px solid rgba(94,200,216,0.35);
  border-radius:12px;
  box-shadow:0 20px 40px rgba(0,0,0,0.4);
}
.stat-card{
  top:-22px; right:-20px; padding:12px 16px;
  transform:translateZ(70px);
  animation:float-bob 5s ease-in-out infinite;
  animation-delay:.3s;
}
.stat-value{ font-family:var(--font-display); font-size:22px; font-weight:700; color:var(--paper); line-height:1; }
.stat-label{ font-family:var(--font-mono); font-size:9px; letter-spacing:.08em; color:var(--slate); margin:4px 0 8px; }
.mini-chart{ width:60px; height:20px; display:block; }
/* The trend line "draws" itself in once, right after the stat card
   arrives — dasharray/dashoffset sized a little past the polyline's
   real length (~64 units) so the stroke is fully hidden at the start. */
.mini-chart polyline{
  stroke-dasharray:70; stroke-dashoffset:70;
  animation:line-draw 1.1s cubic-bezier(.16,1,.3,1) both;
  animation-delay:.9s;
}
@keyframes line-draw{ to{ stroke-dashoffset:0; } }

.cursor-badge{
  left:-18px; bottom:44px; width:44px; height:44px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  border-color:rgba(232,163,61,0.4);
  transform:translateZ(90px);
  animation:float-bob 4.2s ease-in-out infinite;
  animation-delay:.8s;
}
.cursor-badge::before{
  content:''; position:absolute; inset:0; border-radius:50%;
  border:1px solid rgba(232,163,61,0.5);
  animation:ripple 2.6s ease-out infinite;
  animation-delay:1.4s;
}
@keyframes float-bob{
  0%, 100%{ transform:translateZ(70px) translateY(0); }
  50%{ transform:translateZ(70px) translateY(-9px); }
}
@keyframes ripple{
  0%{ transform:scale(1); opacity:.7; }
  100%{ transform:scale(1.6); opacity:0; }
}

@media (max-width:960px){
  .hero-visual-inner{ max-width:380px; }
}
@media (max-width:480px){
  .hero-visual-inner{ max-width:300px; --tiltx:5deg; --tilty:-9deg; }
  .stat-card{ padding:9px 12px; }
  .stat-value{ font-size:18px; }
}

/* ---------- section heads --------------------------------------------
   Shared heading style used by "Studio" and "Selected work" — a title
   on the left, a "SHEET 0X / 04" label on the right. */
/* Anchor jumps (both the browser's own and the work modal's instant
   scrollIntoView) land the target's top edge exactly at the viewport
   top by default — which is right where the fixed nav sits, hiding
   whatever's underneath it. scroll-margin-top pushes the landing spot
   down by the nav's real height, respected automatically by both. */
.section{ padding:clamp(56px, 9vw, 90px) 0; border-top:2px solid var(--grid-line); scroll-margin-top:calc(var(--nav-height) + 20px); }
.section-head{ display:flex; align-items:baseline; justify-content:space-between; margin-bottom:56px; flex-wrap:wrap; gap:16px; }
#about .section-head{ margin-bottom:28px; }
.section-head h2{ font-family:var(--font-display); font-size:34px; font-weight:600; }

/* ---------- about : two-column intro + "what we do" list ------------ */
.about-grid{ display:grid; grid-template-columns:1.15fr 0.85fr; gap:64px; align-items:center; }
.about-panel{
  background:rgba(15, 39, 64, 0.62);
  -webkit-backdrop-filter:blur(16px) saturate(160%);
  backdrop-filter:blur(16px) saturate(160%);
  border:1.5px solid rgba(94, 200, 216, 0.35);
  border-top:1.5px solid rgba(255,255,255,0.26);
  border-radius:16px;
  padding:28px 32px;
  box-shadow:
    0 14px 30px rgba(0,0,0,0.32),
    inset 0 1px 0 rgba(255,255,255,0.12);
}
.about-panel p{ color:var(--slate); font-size:16px; margin-bottom:28px; }

/* The "what we do" list gets the same liquid-glass panel treatment as
   the paragraph above it — same recipe as the nav's scrolled pill —
   so the two read as one consistent glass style, not two different
   panels stacked on top of each other. */
.stack-list{
  display:flex; flex-direction:column; gap:0;
}
/* Two-column grid, not flex space-between: minmax(0,…) tracks + a real
   gutter make long labels/descriptions wrap inside their own column
   instead of colliding (worst case: the "Digitālā identitāte" row). */
.stack-list li{
  list-style:none;
  display:grid; grid-template-columns:minmax(0,auto) minmax(0,1fr);
  gap:4px 24px; align-items:baseline;
  padding:14px 0; border-bottom:1.5px solid rgba(255,255,255,0.10);
  font-family:var(--font-mono); font-size:13px;
}
.stack-list li:last-child{ border-bottom:none; }
.stack-list li span:first-child{ color:var(--paper); }
.stack-list li span:last-child{ color:var(--slate); text-align:right; }
/* A ~180px right-aligned column is unreadable on phones — stack instead. */
@media (max-width:640px){
  .stack-list li{ grid-template-columns:1fr; gap:4px; }
  .stack-list li span:last-child{ text-align:left; }
}

/* stack the two About columns on narrow screens */
@media (max-width:800px){ .about-grid{ grid-template-columns:1fr; gap:40px; } }

/* The phone mockup: a modern device frame (dynamic island, side
   buttons, brushed-metal edge) with an actual mini landing page inside
   — real headline/CTA text and a tab bar, not abstract placeholder
   bars — so it reads as a believable screenshot, not a wireframe. */
.about-visual{
  perspective:1400px;
  display:flex; align-items:center; justify-content:center;
}
.phone-mockup{
  position:relative; width:200px; height:390px;
  border-radius:48px;
  background:linear-gradient(155deg, #33495e 0%, #16283a 55%, #0a1826 100%);
  padding:12px;
  box-shadow:
    0 40px 70px -20px rgba(0,0,0,0.55),
    0 14px 30px rgba(0,0,0,0.35),
    inset 0 0 0 1px rgba(255,255,255,0.06),
    inset 0 1.5px 1px rgba(255,255,255,0.18);
  transform-style:preserve-3d;
  transform:rotateY(14deg) rotateX(-4deg);
  animation:phone-sway 7s ease-in-out infinite;
}
@keyframes phone-sway{
  0%, 100%{ transform:rotateY(14deg) rotateX(-4deg) translateY(0); }
  50%{ transform:rotateY(9deg) rotateX(-1deg) translateY(-8px); }
}

/* Side buttons — thin raised strips on the frame edge, like the
   volume rocker and power button on an actual handset. */
.side-btn{
  position:absolute;
  background:linear-gradient(180deg, #3c5468, #182c3f);
  border-radius:3px;
}
.btn-vol-up{ left:-3px; top:108px; width:3px; height:34px; }
.btn-vol-down{ left:-3px; top:150px; width:3px; height:34px; }
.btn-power{ right:-3px; top:128px; width:3px; height:70px; }

.phone-screen{
  position:relative; height:100%; overflow:hidden;
  border-radius:38px;
  background:#04101c;
  padding:10px 14px 8px;
  display:flex; flex-direction:column;
}

/* The pill-shaped camera/sensor cutout, like current-generation phones */
.dynamic-island{
  position:absolute; top:10px; left:50%; transform:translateX(-50%);
  width:84px; height:24px; border-radius:14px; background:#000; z-index:6;
}

.ps-statusbar{
  display:flex; align-items:center; justify-content:space-between;
  padding:4px 6px 0; margin-bottom:8px; color:var(--paper);
}
.ps-time{ font-family:var(--font-display); font-size:12px; font-weight:600; }
.ps-status-icons{ display:flex; align-items:center; gap:4px; }
.ps-status-icons svg{ height:10px; }

.ps-nav{ display:flex; align-items:center; gap:6px; padding:2px 4px 12px; }
.ps-logo-dot{ width:6px; height:6px; border-radius:50%; background:var(--line); box-shadow:0 0 5px var(--line); flex-shrink:0; }
.ps-logo-word{ font-family:var(--font-mono); font-size:10px; letter-spacing:.06em; color:var(--paper); flex:1; }
.ps-menu{ display:flex; flex-direction:column; gap:3px; width:16px; }
.ps-menu span{ height:2px; border-radius:1px; background:rgba(237,239,242,0.5); }

/* Hero block: gradient "photo" + a dark bottom fade so real overlaid
   text stays legible, same trick real landing pages use. */
.ps-hero{
  position:relative; height:132px; margin-bottom:12px;
  border-radius:14px; overflow:hidden;
  background:linear-gradient(150deg, #6ED4E3 0%, #2E6B86 50%, #0B1E33 100%);
}
.ps-hero::before{
  content:''; position:absolute; inset:0;
  background:linear-gradient(to top, rgba(0,0,0,0.6) 0%, transparent 58%);
}
.ps-hero::after{
  content:''; position:absolute; inset:0;
  background:linear-gradient(115deg, transparent 40%, rgba(255,255,255,0.18) 50%, transparent 60%);
  transform:translateX(-120%);
  animation:shine-sweep 5s ease-in-out infinite;
  animation-delay:2.4s;
}
.ps-badge{
  position:absolute; top:10px; left:10px; z-index:2;
  font-family:var(--font-mono); font-size:8px; font-weight:700; letter-spacing:.06em;
  color:var(--ink); background:var(--amber);
  padding:3px 7px; border-radius:5px;
}
.ps-hero-text{ position:absolute; left:12px; right:12px; bottom:10px; z-index:2; }
.ps-headline{ font-family:var(--font-display); font-size:15px; font-weight:600; color:#fff; line-height:1.15; }
.ps-sub{ font-family:var(--font-body); font-size:10px; color:rgba(255,255,255,0.75); margin-top:3px; }

.ps-buttons{ display:flex; gap:8px; margin-bottom:16px; }
.ps-btn-solid{
  flex:1; height:32px; border-radius:8px; background:var(--line);
  display:flex; align-items:center; justify-content:center;
  font-family:var(--font-mono); font-size:10px; font-weight:700; color:var(--ink);
}
.ps-btn-ghost{ width:32px; height:32px; border-radius:8px; border:1px solid rgba(94,200,216,0.4); }

.ps-feature-row{ display:flex; align-items:center; gap:10px; margin-bottom:auto; }
/* Solid fill + a small glyph — a near-transparent empty box read as a
   rendering glitch rather than a deliberate icon slot. */
.ps-feature-icon{
  width:28px; height:28px; border-radius:8px; flex-shrink:0;
  background:var(--line); border:1px solid rgba(94,200,216,0.6);
  display:flex; align-items:center; justify-content:center;
}
.ps-feature-icon svg{ width:14px; height:14px; }
.ps-feature-lines{ flex:1; }
.ps-feature-lines .bb-line{ margin-bottom:6px; }

/* A real Safari-style toolbar (back / forward / share / bookmark / tabs)
   instead of an app tab bar — this is the detail that signals "website
   viewed in a browser" rather than "native app". */
.safari-toolbar{
  display:flex; justify-content:space-around; align-items:center;
  padding-top:10px; margin-top:10px;
  border-top:1px solid rgba(255,255,255,0.08);
}
.safari-icon{
  width:19px; height:19px; color:rgba(237,239,242,0.55);
  position:relative; display:flex; align-items:center; justify-content:center;
}
.safari-icon svg{ width:100%; height:100%; }
.tabs-icon .tabs-count{
  position:absolute; font-family:var(--font-mono); font-size:8px; font-weight:700;
  color:rgba(237,239,242,0.55);
}

.phone-home-indicator{
  width:60px; height:4px; border-radius:2px;
  background:rgba(237,239,242,0.3); margin:10px auto 0;
}

@media (max-width:800px){
  /* Scaling the whole mockup uniformly, rather than just resizing the
     outer frame, is the fix here: resizing only the frame left
     fixed-size children (the dynamic island, status bar icons) at
     their original size, so at the smaller frame they overlapped the
     time and clipped the wifi icon. A transform scale keeps every
     descendant proportional automatically, so this class of bug can't
     recur as the mockup's internals change. The negative margin
     compensates for the layout space the un-scaled box still reserves
     (transforms are visual-only and don't shrink that reserved space
     on their own). */
  .about-visual{ transform:scale(0.86); margin:-27px 0; }
}

/* ---------- process : "how we work" 4-step strip ---------------------
   Reuses the same numbered-sheet visual language as the Work grid
   (mono-font index in amber, thin grid-line border) so it reads as
   part of the same system, plus a dashed connector between steps on
   wide screens to suggest a single flowing sequence. Steps come from
   script.js's renderProcess(), fed by TRANSLATIONS[lang].process. */
.process-grid{
  display:grid; grid-template-columns:repeat(4, 1fr); gap:28px;
}
@media (max-width:900px){ .process-grid{ grid-template-columns:repeat(2, 1fr); } }
@media (max-width:560px){ .process-grid{ grid-template-columns:1fr; } }
.process-step{
  position:relative; padding:26px 22px 22px; border:2px solid var(--grid-line);
  background:var(--ink-2); transition:transform .25s ease, border-color .25s ease;
}
.process-step:hover{ transform:translateY(-4px); border-color:var(--line); }
/* the connector: a dashed line running left-to-right through every
   step but the first, only shown where steps actually sit side by side */
@media (min-width:561px){
  .process-step:not(:first-child)::before{
    content:''; position:absolute; top:44px; right:100%; width:28px;
    border-top:2px dashed var(--grid-line);
  }
}
/* At the 2-column tablet layout the grid wraps after step 02, so step
   03 starts a new row directly under 01 — it still gets the connector
   above (:not(:first-child) matches it too) but with nothing to its
   left in that row, the stub reads as orphaned. Mirroring it on 02's
   right edge here implies the line continues off-screen and wraps
   around, so both stubs read as one continuous sequence. */
@media (min-width:561px) and (max-width:900px){
  .process-step:nth-child(2)::after{
    content:''; position:absolute; top:44px; left:100%; width:28px;
    border-top:2px dashed var(--grid-line);
  }
}
.process-step-index{
  font-family:var(--font-mono); font-size:22px; font-weight:700;
  color:var(--amber); margin-bottom:14px; letter-spacing:.02em;
}
.process-step h3{ font-family:var(--font-display); font-size:18px; font-weight:600; margin-bottom:8px; }
.process-step p{ color:var(--slate); font-size:14px; line-height:1.5; }

/* ---------- projects : minimal cards ---------------------------------
   The Work grid. Cards (.sheet, an <a> wrapping cover + title + tags)
   are generated by renderProjects() in script.js — the whole card is
   one link to the project's subpage, so there are no inner links. */
#projects-grid{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(320px, 1fr));
  gap:28px;
}
.sheet{
  position:relative; border:2px solid var(--grid-line); background:var(--ink-2);
  padding:26px; transition:transform .25s ease, border-color .25s ease;
  /* Grid rows (and the marquee's flex row) stretch every card to match
     its tallest sibling — a card with a one-line title and one tag
     would otherwise be forced as tall as a neighbor with a two-line
     title and three tags, leaving a dead gap below its own tags. Flex
     column + .tags{margin-top:auto} pushes any such slack up into the
     gap between title and tags instead, so tags always sit flush at
     the card's bottom edge regardless of how much the row stretched it. */
  display:flex; flex-direction:column; color:inherit; /* it's a link now — don't turn the text cyan */
}
.sheet:hover{ transform:translateY(-4px); border-color:var(--line); }
.sheet:hover h3{ color:var(--line); }
.sheet .thumb{
  aspect-ratio:4/3; border:2px solid var(--grid-line); margin-bottom:20px;
  overflow:hidden; position:relative;
  background:var(--ink);
}
.sheet .thumb img{
  width:100%; height:100%; object-fit:cover; display:block;
}
.sheet h3{
  font-family:var(--font-display); font-size:20px; font-weight:600;
  margin-bottom:12px; transition:color .25s ease;
}
.tags{ display:flex; gap:8px; flex-wrap:wrap; }
.sheet .tags{ margin-top:auto; }

/* Homepage "Izvēlētie darbi" marquee (data-mode="marquee", see
   renderProjectsMarquee in script.js) — every project drifts right to
   left in an infinite loop, continuously, without pausing on hover —
   only keyboard focus pauses it (a card a keyboard user has tabbed to
   needs to hold still to be readable/clickable; a mouse hover has no
   such requirement and the client wants the strip to keep moving).
   The mask-image fades cards out near the left/right edges as they
   enter/exit, standing in for a per-card fade animation.
   prefers-reduced-motion skips all of this and falls back to the plain
   static grid below (no .is-marquee class gets added). */
#projects-grid.is-marquee{
  display:block; overflow:hidden;
  padding-block:6px; /* room for the card hover lift */
  /* Full-bleed: break out of .wrap's max-width/padding so the strip runs
     edge-to-edge instead of stopping at the content column. left:50% +
     translateX(-50%) re-centers a 100vw box on the viewport regardless
     of the parent's own width — plain `margin-left:calc(50% - 50vw)`
     would compute that 50% against .wrap's width instead and undershoot. */
  width:100vw;
  position:relative;
  left:50%;
  transform:translateX(-50%);
  -webkit-mask-image:linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
          mask-image:linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
}
.work-marquee-track{
  display:flex; gap:28px; width:max-content;
  animation:work-marquee 100s linear infinite;
}
#projects-grid.is-marquee:focus-within .work-marquee-track{
  animation-play-state:paused;
}
.work-marquee-track .sheet{ flex:0 0 300px; width:300px; }
@media (max-width:700px){
  /* 300px cards leave a narrow phone showing exactly one at a time —
     reading as a broken slider rather than a flowing strip. Narrower
     cards + a tighter gap keep the next card peeking in on both sides,
     so the motion (and the mask fade) actually reads as sliding. */
  .work-marquee-track{ gap:16px; }
  .work-marquee-track .sheet{ flex:0 0 220px; width:220px; padding:18px; }
  .work-marquee-track .sheet h3{ font-size:17px; margin-bottom:8px; }
}
@keyframes work-marquee{
  from{ transform:translateX(0); }
  to{ transform:translateX(-50%); }
}

/* Tag color system: every category gets its own unique color everywhere
   it appears on the site — 16 tag types, 16 colors, spaced around the
   hue wheel — so a visitor can tell SMM-video work from web-design work
   from graphic-design work at a glance, without reading the label.
   (An earlier version reused colors across categories — e.g. video and
   web were both cyan — which defeated that purpose.) Base rule is the
   fallback for an unrecognized/missing type; the data-type rules below
   override it. Add a new category by adding one more [data-type="..."]
   rule here (pick a hue that isn't already taken) and using that type
   in PROJECTS. */
.tag{
  font-family:var(--font-mono); font-size:11px;
  padding:4px 9px; border-radius:5px;
  color:var(--line); border:1px solid var(--line); background:transparent;
}
.tag[data-type="web"]{         color:#5EC8D8; border-color:rgba(94,200,216,0.5);  background:rgba(94,200,216,0.08); }
.tag[data-type="uxui"]{        color:#A78BFA; border-color:rgba(167,139,250,0.5); background:rgba(167,139,250,0.08); }
.tag[data-type="branding"]{    color:#F472B6; border-color:rgba(244,114,182,0.5); background:rgba(244,114,182,0.08); }
.tag[data-type="design-system"]{ color:#6FCF97; border-color:rgba(111,207,151,0.5); background:rgba(111,207,151,0.08); }
.tag[data-type="seo"]{         color:#60A5FA; border-color:rgba(96,165,250,0.5);  background:rgba(96,165,250,0.08); }
/* …and purpose/field tags for the web portfolio, one per project —
   mirrors the SMM purpose tags below (spa/restorans/etc.) */
.tag[data-type="eu-project"]{  color:#74D161; border-color:rgba(116,209,97,0.5);  background:rgba(116,209,97,0.08); }
.tag[data-type="finance"]{     color:#6C72E0; border-color:rgba(108,114,224,0.5); background:rgba(108,114,224,0.08); }
.tag[data-type="marketing"]{   color:#E96384; border-color:rgba(233,99,132,0.5);  background:rgba(233,99,132,0.08); }
.tag[data-type="industry"]{    color:#A66B3F; border-color:rgba(166,107,63,0.5);  background:rgba(166,107,63,0.08); }
.tag[data-type="interactive"]{ color:#A639EF; border-color:rgba(166,57,239,0.5);  background:rgba(166,57,239,0.08); }
/* SMM portfolio taxonomy — format tags… */
.tag[data-type="video"]{       color:#F87171; border-color:rgba(248,113,113,0.5); background:rgba(248,113,113,0.08); }
.tag[data-type="grafika"]{     color:#A3E635; border-color:rgba(163,230,53,0.5);  background:rgba(163,230,53,0.08); }
/* …and purpose/field tags */
.tag[data-type="spa"]{         color:#5CD6B8; border-color:rgba(92,214,184,0.5);  background:rgba(92,214,184,0.08); }
.tag[data-type="restorans"]{   color:#FB923C; border-color:rgba(251,146,60,0.5);  background:rgba(251,146,60,0.08); }
.tag[data-type="pasakumi"]{    color:#FACC15; border-color:rgba(250,204,21,0.5);  background:rgba(250,204,21,0.08); }
.tag[data-type="kampana"]{     color:#E879F9; border-color:rgba(232,121,249,0.5); background:rgba(232,121,249,0.08); }
/* Internal "draft texts" marker — amber + dashed so the team can't miss
   it, styled unlike any real category tag. Never appears in the filter. */
.tag.tag-draft{ color:var(--amber); border:1px dashed var(--amber); background:rgba(232,163,61,0.12); }

/* ---------- work.html filters -----------------------------------------
   A glass pill button that opens a glass panel directly below it, in
   normal document flow — not a floating overlay. An earlier version
   floated the panel via position:absolute so it wouldn't push the grid
   down, but that meant it sat *in front of* the first row of project
   cards instead, hiding them behind it. Flowing normally costs nothing
   here: the grid simply starts right after the panel ends, so the two
   can never overlap, and "aligns with the top of the project boxes" is
   just what normal flow does automatically. Made deliberately wide
   (spans the full content column) so the tag pills wrap into as few
   rows as possible — short and wide, not a tall narrow column. */
.work-filter-wrap{ margin-bottom:40px; }
.work-filter-toggle{
  display:inline-flex; align-items:center; gap:9px;
  font-family:var(--font-mono); font-size:13px; letter-spacing:.03em;
  color:var(--paper);
  background:rgba(15, 39, 64, 0.5);
  -webkit-backdrop-filter:blur(18px) saturate(170%);
  backdrop-filter:blur(18px) saturate(170%);
  border:1px solid rgba(255,255,255,0.14);
  border-top-color:rgba(255,255,255,0.26);
  padding:10px 16px; border-radius:999px; cursor:pointer;
  box-shadow:0 8px 20px rgba(0,0,0,0.24), inset 0 1px 0 rgba(255,255,255,0.10);
  margin-bottom:14px;
  transition:background .2s ease, border-color .2s ease;
}
.work-filter-toggle:hover{ border-color:rgba(94,200,216,0.5); }
.work-filter-toggle-badge{
  display:inline-flex; align-items:center; justify-content:center;
  min-width:18px; height:18px; padding:0 5px;
  background:var(--line); color:var(--ink); font-size:11px; font-weight:700;
  border-radius:999px;
}
/* display:inline-flex above beats the UA stylesheet's [hidden]{display:none}
   at equal specificity (source order), so the badge would stay visible
   with the JS-set `hidden` attribute otherwise — this reinstates it. */
.work-filter-toggle-badge[hidden]{ display:none; }
.work-filter-toggle-chevron{ transition:transform .2s ease; flex-shrink:0; }
.work-filter-toggle[aria-expanded="true"] .work-filter-toggle-chevron{ transform:rotate(180deg); }
.work-filter{
  display:none; flex-direction:column; gap:12px;
  width:100%; box-sizing:border-box;
  padding:18px; border-radius:16px;
  background:rgba(8, 20, 35, 0.97);
  -webkit-backdrop-filter:blur(28px) saturate(180%);
  backdrop-filter:blur(28px) saturate(180%);
  border:1px solid rgba(255,255,255,0.10);
  border-top-color:rgba(255,255,255,0.24);
  box-shadow:0 20px 45px rgba(0,0,0,0.45), inset 0 1px 0 rgba(255,255,255,0.12);
}
.work-filter.is-open{ display:flex; }
.work-service-filter{
  display:inline-flex; gap:4px; padding:4px; align-self:flex-start;
  background:rgba(255,255,255,0.05);
  border:1px solid rgba(255,255,255,0.08);
  border-radius:999px;
}
.work-service-filter .filter-btn{
  background:transparent; border-color:transparent; padding:6px 14px; font-size:12px;
}
.work-filter-tags{ display:flex; gap:8px; flex-wrap:wrap; }
.work-filter .filter-btn{ padding:7px 13px; font-size:12px; }
.filter-btn{
  font-family:var(--font-mono); font-size:13px; letter-spacing:.03em;
  color:var(--slate); background:rgba(11,30,51,0.35);
  border:1px solid rgba(255,255,255,0.10);
  padding:9px 18px; border-radius:999px; cursor:pointer;
  display:inline-flex; align-items:center; gap:8px;
  transition:background .2s ease, color .2s ease, border-color .2s ease;
}
.filter-btn:hover{ color:var(--paper); border-color:rgba(94,200,216,0.5); }
.filter-btn.is-active{ background:var(--line); color:var(--ink); font-weight:700; border-color:var(--line); }
.filter-btn .filter-count{
  font-size:11px; opacity:.75;
  border-left:1px solid currentColor; padding-left:8px;
}

/* ---------- project subpage (project.html?p=slug) ---------------------
   Rendered by renderProjectPage() in script.js into #project-root.
   The videos are vertical (9:16 Instagram reels), so they're capped by
   height and centered rather than stretched to the column width. */
.project-hero{ padding-bottom:0; }
.project-top{ max-width:760px; }
.project-back{
  display:inline-block; font-family:var(--font-mono); font-size:13px;
  color:var(--slate); margin-bottom:28px; transition:color .2s ease;
}
.project-back:hover{ color:var(--line); }
/* Glass card behind the project header text (title/tags/description) —
   same material family as .about-panel and the contact modal. The back
   link stays outside, on the bare blueprint background. */
.project-panel{
  background:rgba(15, 39, 64, 0.45);
  -webkit-backdrop-filter:blur(20px) saturate(170%);
  backdrop-filter:blur(20px) saturate(170%);
  border:1px solid rgba(255,255,255,0.12); border-top-color:rgba(255,255,255,0.26);
  border-radius:16px;
  padding:clamp(24px, 4vw, 36px);
  box-shadow:0 14px 30px rgba(0,0,0,0.32), inset 0 1px 0 rgba(255,255,255,0.12);
  margin-bottom:48px;
}
.project-top h1{
  font-family:var(--font-display); font-weight:600;
  font-size:clamp(28px, 4.5vw, 44px); line-height:1.15; margin-bottom:18px;
}
.project-top .tags{ margin-bottom:22px; }
.project-draft-note{
  font-family:var(--font-mono); font-size:13px; color:var(--amber);
  border:1px dashed var(--amber); border-radius:8px;
  background:rgba(232,163,61,0.08);
  padding:12px 16px; margin-bottom:22px;
}
.project-desc{ color:var(--slate); font-size:16px; line-height:1.7; }
/* Contact-sheet strip: every item in a row shares one height, so a 9:16
   reel reads as a narrow column, a banner as a wide plate, a square as a
   square — widths simply fall out of each medium's intrinsic ratio. */
.project-media{
  display:flex; flex-wrap:wrap; gap:24px; align-items:stretch;
  justify-content:center;
  margin-bottom:56px;
}
.pm-item{
  margin:0; border:2px solid var(--grid-line); background:var(--ink-2);
  overflow:hidden; height:clamp(320px, 58vh, 560px); max-width:100%;
}
/* Each video's real ratio is measured from its poster by
   layoutProjectMedia() in script.js and written to --pm-ar (CSP-safe
   setProperty); 9/16 is the fallback until the poster reports in, and
   reserving a box up front kills the layout shift either way. Wide
   16:9 clips no longer get squeezed into a portrait card. */
.pm-video{ aspect-ratio:var(--pm-ar, 9/16); }
.pm-video .vp{ height:100%; }
.pm-image{ display:block; }
.pm-zoom, .pm-external{
  display:block; height:100%; padding:0; border:0; background:none;
  cursor:zoom-in;
}
.pm-zoom img, .pm-external img{
  display:block; height:100%; width:auto; max-width:100%; object-fit:contain;
}
/* .pm-external: projects that carry a liveUrl link straight out to the
   real site instead of opening the lightbox — cursor reads as a normal
   link, and a glass label fades in on hover/focus to make that obvious
   before the click (matching .bb-overlay-cta's look elsewhere on site). */
.pm-external{ position:relative; cursor:pointer; text-decoration:none; max-width:100%; }
.pm-external-label{
  position:absolute; left:50%; bottom:16px; transform:translate(-50%, 6px);
  display:flex; align-items:center; gap:7px;
  max-width:calc(100% - 24px); box-sizing:border-box;
  background:rgba(15,39,64,0.72);
  -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
  border:1px solid rgba(255,255,255,0.25); border-radius:8px;
  color:var(--paper); font-family:var(--font-mono); font-size:12px;
  padding:9px 16px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  opacity:0; transition:opacity .2s ease, transform .2s ease;
  pointer-events:none;
}
.pm-external:hover .pm-external-label,
.pm-external:focus-visible .pm-external-label{ opacity:1; transform:translate(-50%, 0); }
.pm-external:focus-visible{ outline:2px solid var(--line); outline-offset:2px; }
/* Justified single row: layoutProjectMedia() in script.js computes a
   shared row height (--pm-row-h) and each item's aspect ratio (--pm-ar);
   widths fall out. Set via el.style.setProperty — the CSP has no
   'unsafe-inline' in style-src, so inline style attributes are no-ops. */
.project-media.is-row{ flex-wrap:nowrap; }
.project-media.is-row .pm-item{
  height:var(--pm-row-h);
  /* border-box: subtract the 2px border on each side before scaling */
  width:calc((var(--pm-row-h) - 4px) * var(--pm-ar) + 4px);
  flex:0 0 auto;
}
.project-media.is-row .pm-zoom img,
.project-media.is-row .pm-external img{ width:100%; height:100%; object-fit:contain; }
@media (max-width:640px){
  .pm-item{ height:auto; width:100%; }
  /* min(100%, 360px), not vw — vw measures the raw viewport, ignoring
     .wrap's 32px side padding, so a video could render wider than the
     column it's actually sitting in and visibly spill past its card. */
  .pm-video{ width:min(100%, 360px); height:auto; margin-inline:auto; }
  .pm-zoom, .pm-external{ height:auto; }
  .pm-zoom img, .pm-external img{ width:100%; height:auto; max-width:100%; }
  /* No hover on touch — show the label at rest instead of hidden behind
     a hover state that will never fire. position:static replaces the
     desktop rule's left:50% centering with normal-flow margin:auto
     centering, but transform:translateX(-50%) doesn't need `position`
     to apply — left uncleared, it shifted the already-centered label
     another half-its-own-width to the left, pushing it out of the card. */
  .pm-external-label{ opacity:1; transform:none; position:static; margin:10px auto 0; width:fit-content; max-width:100%; }
}
.project-pager{
  display:flex; justify-content:space-between; align-items:stretch; gap:20px;
  border-top:2px solid var(--grid-line); padding:28px 0 56px;
}
/* First/last projects emit an empty placeholder slot — hide it; a lone
   next-card still sits right via its margin-left:auto. */
.project-pager > span:empty{ display:none; }
/* Arrow-shaped glass cards: the card itself points in the travel
   direction via clip-path (prev ← left tip, next → right tip). A real
   border would be cut by the clip, so the rim is layered instead: the
   anchor's own background is the rim color (same cyan frame as the
   .pm-item video previews) and a ::before inset by 2px carries the
   glass fill, re-clipped to the same arrow. Keyboard focus reuses the
   hover brightening (outlines are clipped too). */
.project-pager a{
  position:relative;
  font-family:var(--font-mono); font-size:13px; color:var(--slate);
  display:flex; flex-direction:column; gap:6px; justify-content:center;
  flex:0 1 340px; max-width:48%;
  padding:16px 20px 16px 48px;
  background:var(--grid-line);
  clip-path:polygon(28px 0, 100% 0, 100% 100%, 28px 100%, 0 50%);
  transition:background .2s ease, color .2s ease;
}
.project-pager a::before{
  /* Mostly opaque fill: the rim layer underneath is part of this
     layer's backdrop, so a see-through glass here would tint the whole
     card cyan. 0.9 keeps the rim crisp with just a hint of depth. */
  content:""; position:absolute; inset:2px; z-index:-1;
  background:rgba(11, 30, 51, 0.9);
  -webkit-backdrop-filter:blur(16px) saturate(160%);
  backdrop-filter:blur(16px) saturate(160%);
  clip-path:polygon(28px 0, 100% 0, 100% 100%, 28px 100%, 0 50%);
  transition:background .2s ease;
}
.project-pager a:hover,
.project-pager a:focus-visible{
  background:rgba(94, 200, 216, 0.75); color:var(--line);
}
.project-pager a:hover::before,
.project-pager a:focus-visible::before{
  background:rgba(20, 48, 76, 0.9);
}
.project-pager a span{
  color:var(--paper); font-family:var(--font-display); font-size:15px; line-height:1.35;
}
.project-pager .pager-next{
  text-align:right; margin-left:auto;
  padding:16px 48px 16px 20px;
  clip-path:polygon(0 0, calc(100% - 28px) 0, 100% 50%, calc(100% - 28px) 100%, 0 100%);
}
.project-pager .pager-next::before{
  clip-path:polygon(0 0, calc(100% - 28px) 0, 100% 50%, calc(100% - 28px) 100%, 0 100%);
}
@media (max-width:640px){
  .project-pager{ flex-direction:column; }
  .project-pager a{ flex-basis:auto; max-width:none; width:100%; }
}

/* The "reserved" slot at the end of the work grid. Visually it should read
   as "more is coming" rather than "this is unfinished" — a brighter dashed
   line, a soft glow, and copy written for visitors, not for us. */
.empty-slot{
  border:2px dashed rgba(94, 200, 216, 0.55);
  border-radius:4px;
  display:flex; align-items:center; justify-content:center;
  min-height:280px; font-family:var(--font-mono); font-size:13px;
  color:var(--line); text-align:center; padding:30px;
  background:rgba(94, 200, 216, 0.04);
  box-shadow:inset 0 0 24px rgba(94, 200, 216, 0.06);
  transition:border-color .25s ease, background .25s ease;
}
.empty-slot:hover{
  border-color:rgba(94, 200, 216, 0.9);
  background:rgba(94, 200, 216, 0.07);
}
.empty-slot .slot-title{ color:var(--paper); font-size:14px; margin-bottom:6px; letter-spacing:.05em; }
.empty-slot .slot-sub{ color:var(--slate); font-size:12px; }

/* ---------- contact / footer ---------- */
/* Full-bleed liquid glass — <footer> itself has no max-width (only
   the .wrap inside it does), so a background here automatically spans
   edge to edge while the content stays centered, same relationship
   the nav has between its full-bleed bar and its own inner content. */
footer{
  padding:clamp(28px, 4vw, 36px) 0 20px;
  background:rgba(9, 22, 38, 0.5);
  -webkit-backdrop-filter:blur(24px) saturate(170%);
  backdrop-filter:blur(24px) saturate(170%);
  border-top:1.5px solid rgba(94, 200, 216, 0.35);
  box-shadow:inset 0 1px 0 rgba(255,255,255,0.06);
  scroll-margin-top:calc(var(--nav-height) + 20px);
}
.contact-inner{ display:flex; justify-content:space-between; align-items:flex-end; flex-wrap:wrap; gap:28px; }
footer h2{ font-family:var(--font-display); font-size:clamp(22px, 3.4vw, 32px); font-weight:600; max-width:520px; }

/* A real footer: brand + tagline, a sitemap, and contact details —
   sitting between the big "have a project in mind" CTA above and the
   copyright bar below, instead of jumping straight from one to the other. */
.footer-columns{
  display:grid; grid-template-columns:1.4fr 1fr; gap:32px;
  margin-top:24px; padding-top:18px;
  border-top:1px solid rgba(94, 200, 216, 0.2);
}
.footer-col-brand .logo{ margin-bottom:14px; }
.footer-tagline{ color:var(--slate); font-size:13px; line-height:1.55; max-width:280px; }
.footer-col-title{
  font-family:var(--font-mono); font-size:11px; letter-spacing:.1em;
  text-transform:uppercase; color:var(--slate); margin-bottom:12px;
}
.footer-email{
  display:block; font-family:var(--font-mono); font-size:13px;
  color:var(--line); margin-bottom:10px; transition:color .2s;
}
.footer-email:hover{ color:var(--paper); }
.footer-meta-line{ font-family:var(--font-body); font-size:12.5px; color:var(--slate); margin-bottom:5px; }

@media (max-width:700px){
  .footer-columns{ grid-template-columns:1fr; gap:32px; }
}

.foot-meta{ font-family:var(--font-mono); font-size:12px; color:var(--slate); display:flex; gap:28px; margin-top:18px; flex-wrap:wrap; }

/* ---------- contact modal --------------------------------------------
   Opened by any .js-open-contact link (the nav's "Kontakti") instead of
   jumping to the footer — see initContactModal in script.js. Reuses
   the same eyebrow/heading pattern as the footer CTA so it doesn't
   feel like a different piece of the site. */
.contact-modal{
  position:fixed; inset:0; z-index:200;
  display:flex; align-items:center; justify-content:center; padding:24px;
  opacity:0; visibility:hidden; pointer-events:none;
  transition:opacity .3s ease, visibility .3s;
}
.contact-modal.is-open{ opacity:1; visibility:visible; pointer-events:auto; }
.contact-modal-backdrop{
  position:absolute; inset:0;
  background:rgba(5,14,26,0.75);
  -webkit-backdrop-filter:blur(4px); backdrop-filter:blur(4px);
}
.contact-modal-panel{
  position:relative; max-width:560px; width:100%;
  background:rgba(15, 39, 64, 0.4);
  -webkit-backdrop-filter:blur(24px) saturate(180%);
  backdrop-filter:blur(24px) saturate(180%);
  border:1px solid rgba(255,255,255,0.10); border-top-color:rgba(255,255,255,0.26);
  border-radius:20px;
  padding:clamp(32px, 5vw, 48px);
  box-shadow:0 30px 60px rgba(0,0,0,0.45), inset 0 1px 0 rgba(255,255,255,0.12), inset 0 -1px 0 rgba(0,0,0,0.16);
  transform:translateY(16px) scale(.97);
  transition:transform .3s cubic-bezier(.16,1,.3,1);
}
.contact-modal.is-open .contact-modal-panel{ transform:translateY(0) scale(1); }
.contact-modal-panel h2{
  font-family:var(--font-display); font-size:clamp(24px, 3.6vw, 34px);
  font-weight:600; margin:16px 0 12px;
}
.contact-modal-lede{ color:var(--slate); font-size:15px; margin-bottom:26px; }

/* The homepage's teaser grid ends with this — a plain centered CTA
   pointing to the full work.html listing. */
.work-viewall-row{ display:flex; justify-content:center; margin-top:40px; }

.contact-modal-close{
  position:absolute; top:16px; right:16px; width:36px; height:36px;
  border-radius:50%; background:rgba(255,255,255,0.06);
  border:1px solid rgba(255,255,255,0.14); color:var(--paper);
  font-size:20px; line-height:1; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  transition:background .2s ease;
}
.contact-modal-close:hover{ background:rgba(255,255,255,0.14); }

@media (prefers-reduced-motion: reduce){
  .contact-modal, .contact-modal-panel{ transition:opacity .15s linear; }
  .contact-modal-panel{ transform:none !important; }
}

/* ---------- custom video player (.vp) --------------------------------
   Built by buildVideoPlayer() in script.js. Replaces the browser's
   native controls entirely (no playback-speed control, no overflow
   menu) with a minimal themed bar: play/pause, mono time readout,
   seek, mute, volume. Two contexts share the markup: "page" (inside
   .pm-video in the project gallery) and "lightbox" (inside the media
   lightbox stage). */
.vp{ position:relative; background:var(--ink); }
.vp .vp-video{ display:block; width:100%; height:100%; object-fit:cover; }
.vp[data-context="page"] .vp-video,
.vp[data-context="page"] .vp-bigplay{ cursor:pointer; }

/* Big center badge — the resting/paused affordance. Same glass recipe
   as the contact modal so the two overlays feel like one material. */
.vp-bigplay{
  position:absolute; inset:0; margin:auto; width:64px; height:64px;
  display:flex; align-items:center; justify-content:center;
  border-radius:50%;
  background:rgba(15,39,64,0.55);
  -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
  border:1px solid rgba(255,255,255,0.22);
  color:var(--cyan); cursor:pointer;
  transition:transform .2s ease, opacity .2s ease, background .2s ease;
}
.vp-bigplay svg{ margin-left:3px; }
.vp-bigplay:hover{ background:rgba(15,39,64,0.78); transform:scale(1.06); }
.vp.is-playing .vp-bigplay{ opacity:0; pointer-events:none; }

/* Floating liquid-glass pill, same material family as .vp-bigplay and
   the contact-modal panel: tinted navy base + blur/saturate + brighter
   top border. The dark tint keeps controls legible over any frame. */
.vp-controls{
  position:absolute; left:12px; right:12px; bottom:12px;
  display:flex; align-items:center; gap:8px;
  padding:8px 14px;
  border-radius:999px;
  background:rgba(15,39,64,0.6);
  -webkit-backdrop-filter:blur(16px) saturate(160%);
  backdrop-filter:blur(16px) saturate(160%);
  border:1px solid rgba(255,255,255,0.14); border-top-color:rgba(255,255,255,0.26);
  box-shadow:0 8px 24px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.10);
  transition:opacity .25s ease, transform .25s ease;
}
/* Auto-hide while playing and idle — script.js toggles .is-idle. */
.vp.is-idle .vp-controls{ opacity:0; transform:translateY(10px); pointer-events:none; }
@media (prefers-reduced-motion: reduce){
  .vp-controls{ transition:opacity .25s ease; }
  .vp.is-idle .vp-controls{ transform:none; }
}
.vp.is-idle{ cursor:none; }

.vp-controls button{
  background:none; border:0; color:var(--paper); cursor:pointer;
  width:30px; height:30px; flex-shrink:0; border-radius:6px;
  display:flex; align-items:center; justify-content:center;
  transition:color .2s ease;
}
.vp-controls button:hover{ color:var(--cyan); }
/* icon swaps driven by state classes on .vp */
.vp .vp-ic-pause{ display:none; }
.vp.is-playing .vp-ic-play{ display:none; }
.vp.is-playing .vp-ic-pause{ display:block; }
.vp .vp-ic-muted{ display:none; }
.vp.is-muted .vp-ic-vol{ display:none; }
.vp.is-muted .vp-ic-muted{ display:block; }

.vp-time{
  font-family:var(--font-mono); font-size:11px; color:var(--paper);
  flex-shrink:0; white-space:nowrap; opacity:.9;
}
.vp-seek{ flex:1 1 auto; min-width:40px; }
/* width + min-width:0 both matter: a bare flex-basis gets floored at the
   range input's intrinsic min-content (~130px), which pushed the slider
   out of the pill on narrow 9:16 lightbox videos. */
.vp-vol{ flex:0 0 56px; width:56px; min-width:0; }
/* Page-context previews are poster + big-play only — every click routes
   to the lightbox (delegated handler in script.js), where the full
   control pill lives. iOS ignores JS volume anyway, so coarse pointers
   also drop the volume slider in the lightbox. */
.vp[data-context="page"] .vp-controls{ display:none; }
@media (pointer:coarse){ .vp-vol{ display:none; } }
/* Narrow lightbox frames (classes set by a ResizeObserver in
   buildVideoPlayer) shed the volume slider, then the time readout,
   so the pill never spills past the video edge. */
.vp.vp-narrow .vp-vol{ display:none; }
.vp.vp-tight .vp-time{ display:none; }

/* Themed range inputs (seek + volume). Two-tone cyan fill is driven by
   script.js writing --vp-fill via el.style.setProperty (CSP-safe). */
.vp input[type=range]{
  -webkit-appearance:none; appearance:none;
  height:4px; border-radius:2px; cursor:pointer; margin:0;
  background:linear-gradient(to right,
    var(--cyan) var(--vp-fill, 0%),
    rgba(255,255,255,0.18) var(--vp-fill, 0%));
}
.vp input[type=range]:disabled{ cursor:default; opacity:.45; }
.vp input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none; width:12px; height:12px; border-radius:50%;
  background:var(--cyan); border:2px solid var(--ink);
  box-shadow:0 0 0 1px rgba(94,200,216,0.4);
  transition:transform .15s ease;
}
.vp input[type=range]:hover::-webkit-slider-thumb,
.vp input[type=range]:focus-visible::-webkit-slider-thumb{ transform:scale(1.25); }
.vp input[type=range]::-moz-range-thumb{
  width:12px; height:12px; border-radius:50%;
  background:var(--cyan); border:2px solid var(--ink);
}
.vp input[type=range]::-moz-range-track{ height:4px; background:transparent; }
.vp input[type=range]:focus-visible{ outline:2px solid var(--cyan); outline-offset:3px; }

/* ---------- media lightbox --------------------------------------------
   One JS-injected instance (ensureLightbox in script.js). Clicked media
   appears centered at its intrinsic aspect ratio; all the unused screen
   area gets the same liquid-glass treatment as the contact modal. */
.media-lightbox{
  position:fixed; inset:0; z-index:210;
  display:flex; align-items:center; justify-content:center; padding:24px;
  opacity:0; visibility:hidden; pointer-events:none;
  transition:opacity .3s ease, visibility .3s;
}
.media-lightbox.is-open{ opacity:1; visibility:visible; pointer-events:auto; }
.media-lightbox-backdrop{
  position:absolute; inset:0;
  background:rgba(5,14,26,0.78);
  -webkit-backdrop-filter:blur(14px) saturate(160%);
  backdrop-filter:blur(14px) saturate(160%);
}
/* Close button sits at viewport top-right, outside the panel (the panel
   is transform-animated, which would hijack position:fixed inside it). */
.media-lightbox-close{
  position:absolute; top:20px; right:24px; z-index:2;
  width:40px; height:40px; border-radius:50%;
  background:transparent; border:1px solid transparent;
  color:rgba(255,255,255,0.55); cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  transition:background .2s ease, color .2s ease, border-color .2s ease;
}
.media-lightbox-close svg{ pointer-events:none; }
.media-lightbox-close:hover,
.media-lightbox-close:focus-visible{
  background:rgba(255,255,255,0.10);
  -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
  border-color:rgba(255,255,255,0.18);
  color:var(--paper);
}
.media-lightbox-close:focus-visible{ outline:2px solid var(--cyan); outline-offset:2px; }
.media-lightbox-panel{
  position:relative; max-width:100%; max-height:100%;
  display:flex; align-items:center; justify-content:center;
  transform:translateY(16px) scale(.97);
  transition:transform .3s cubic-bezier(.16,1,.3,1);
}
.media-lightbox.is-open .media-lightbox-panel{ transform:translateY(0) scale(1); }
/* The media itself IS the panel — chromeless, intrinsic ratio, fits the
   viewport (9:16 reels cap at 86vh and stay centered). */
.media-lightbox-stage img,
.media-lightbox-stage .vp-video{
  display:block; max-width:min(92vw, 1100px); max-height:86vh;
  width:auto; height:auto;
}
.media-lightbox-stage img{ border:2px solid var(--grid-line); background:var(--ink-2); cursor:zoom-in; }
.media-lightbox-stage .vp{ border:2px solid var(--grid-line); }
.media-lightbox-stage .vp .vp-video{ object-fit:initial; }
/* Zoom toggle (click the image): enlarges it to a moderate multiple of
   its fit size (set inline via JS, not full native resolution) and
   scrolls so the clicked point is centered — then the real scrollbar
   takes over for panning to the rest of the image, rather than forcing
   a fresh click-elsewhere each time. */
.media-lightbox-stage{ overflow:hidden; }
.media-lightbox-stage.is-zoomed{
  overflow:auto; -webkit-overflow-scrolling:touch;
  max-width:96vw; max-height:92vh;
}
.media-lightbox-stage.is-zoomed img{ max-width:none; max-height:none; cursor:zoom-out; }

@media (max-width:640px){
  .media-lightbox{ padding:12px; }
  .media-lightbox-close{ top:12px; right:12px; }
  .media-lightbox-stage img,
  .media-lightbox-stage .vp-video{ max-width:94vw; max-height:82vh; }
}
@media (prefers-reduced-motion: reduce){
  .media-lightbox, .media-lightbox-panel{ transition:opacity .15s linear; }
  .media-lightbox-panel{ transform:none !important; }
  .vp-controls, .vp-bigplay{ transition:none; }
}

/* ==================================================================
   PRICING PAGE
   Card grids for the Web and SMM tiers (rendered by script.js from
   the PRICING object), plus the bundle banner underneath. Cards reuse
   the same visual language as the project "sheet" cards (2px grid-line
   border, ink-2 fill, lift-on-hover) so the page doesn't feel like a
   separate design system bolted onto the site.
   ================================================================== */
.pricing-grid{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(280px, 1fr));
  gap:28px;
}
.price-card{
  display:flex; flex-direction:column;
  position:relative; border:2px solid var(--grid-line); background:var(--ink-2);
  padding:26px; transition:transform .25s ease, border-color .25s ease, box-shadow .25s ease;
}
.price-card:hover{ transform:translateY(-4px); border-color:var(--line); }

/* the recommended tier in each group gets the full accent border + a
   soft glow, plus a filled "most chosen" pill — same visual weight as
   the primary button elsewhere on the site */
.price-card.is-featured{
  border-color:var(--line);
  box-shadow:0 0 0 1px rgba(94,200,216,0.15), 0 20px 40px -20px rgba(94,200,216,0.25);
}
.price-card-head{ display:flex; align-items:center; justify-content:space-between; margin-bottom:14px; }
.price-tag{ font-family:var(--font-mono); font-size:11px; letter-spacing:.1em; color:var(--slate); }
.price-badge{
  font-family:var(--font-mono); font-size:10px; font-weight:700; letter-spacing:.05em;
  color:var(--ink); background:var(--line); padding:4px 10px; border-radius:999px;
}
.price-name{ font-family:var(--font-display); font-size:20px; font-weight:600; margin-bottom:8px; }
.price-desc{ color:var(--slate); font-size:14px; margin-bottom:20px; }
.price-value{ display:flex; align-items:baseline; gap:6px; margin-bottom:20px; }
.price-from{ font-family:var(--font-mono); font-size:12px; color:var(--slate); text-transform:uppercase; letter-spacing:.05em; }
.price-amount{ font-family:var(--font-display); font-size:30px; font-weight:700; color:var(--paper); }
.price-unit{ font-family:var(--font-mono); font-size:12px; color:var(--slate); }
.price-includes{ list-style:none; display:flex; flex-direction:column; gap:10px; margin-bottom:24px; }
.price-includes li{
  font-size:13px; color:var(--paper); padding-left:20px; position:relative;
}
.price-includes li::before{
  content:'✓'; position:absolute; left:0; color:var(--line); font-weight:700;
}

/* The one-time package (tier.wide in script.js) spans the full grid
   width instead of sitting alone as an odd fourth card the same size
   as the three monthly tiers. Same card, same child elements — just
   reflowed into two columns (info/CTA on the left, feature list on
   the right) via grid-template-areas, so no template/JS changes are
   needed to support it. */
.price-card-wide{
  grid-column:1 / -1;
  display:grid;
  grid-template-columns:1fr 1.3fr;
  grid-template-areas:
    "head head"
    "name includes"
    "desc includes"
    "value includes"
    "cta includes";
  column-gap:40px; row-gap:10px;
}
.price-card-wide .price-card-head{ grid-area:head; }
.price-card-wide .price-name{ grid-area:name; }
.price-card-wide .price-desc{ grid-area:desc; }
.price-card-wide .price-value{ grid-area:value; }
.price-card-wide .price-includes{ grid-area:includes; align-self:start; margin-bottom:0; }
.price-card-wide .price-cta{ grid-area:cta; align-self:end; width:fit-content; margin-top:8px; }

@media (max-width:700px){
  .price-card-wide{
    grid-template-columns:1fr;
    grid-template-areas:
      "head" "name" "desc" "value" "includes" "cta";
  }
  .price-card-wide .price-cta{ width:100%; }
}
.price-cta{ margin-top:auto; justify-content:center; text-align:center; }

/* ---------- team : profile cards --------------------------------------
   Same bordered-card language as the pricing/project cards, built
   around a circular initials avatar since we don't have real photos
   yet — swap .team-avatar's text for an <img> once photos exist, no
   other markup changes needed. */
.team-grid{
  display:grid; grid-template-columns:repeat(auto-fit, minmax(260px, 1fr));
  gap:28px;
}
.team-card{
  border:2px solid var(--grid-line); background:var(--ink-2);
  padding:28px; transition:transform .25s ease, border-color .25s ease;
}
.team-card:hover{ transform:translateY(-4px); border-color:var(--line); }
.team-avatar{
  width:72px; height:72px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  background:rgba(94,200,216,0.12); border:1px solid rgba(94,200,216,0.35);
  font-family:var(--font-mono); font-size:18px; font-weight:700; color:var(--line);
  margin-bottom:18px;
  overflow:hidden; /* clips the photo variant to the circle */
}
.team-avatar.has-photo{ background:none; border-color:rgba(255,255,255,0.15); }
.team-avatar img{ width:100%; height:100%; object-fit:cover; display:block; }
.team-name{ font-family:var(--font-display); font-size:20px; font-weight:600; margin-bottom:6px; }
.team-role{ font-family:var(--font-mono); font-size:12px; color:var(--line); margin-bottom:14px; }
.team-bio{ color:var(--slate); font-size:14px; line-height:1.6; }
.team-certs{ margin-top:14px; display:flex; flex-wrap:wrap; align-items:center; gap:8px; }
.team-cert-label{
  font-family:var(--font-mono); font-size:11px; color:var(--slate);
  text-transform:uppercase; letter-spacing:.04em;
}
.cert-badge{
  font-family:var(--font-mono); font-size:11px;
  padding:4px 9px; border-radius:5px;
  color:#5EC8D8; border:1px solid rgba(94,200,216,0.5); background:rgba(94,200,216,0.08);
  cursor:zoom-in; transition:background-color .2s ease, border-color .2s ease;
}
.cert-badge:hover, .cert-badge:focus-visible{
  background:rgba(94,200,216,0.18); border-color:rgba(94,200,216,0.8);
}
.cert-badge:focus-visible{ outline:2px solid var(--line); outline-offset:2px; }

.price-footnote{ font-family:var(--font-mono); font-size:12.5px; color:var(--slate); margin-top:24px; max-width:640px; }

/* Add-on list: a plain row-based list (name + description on the left,
   price on the right) rather than another card grid — these are
   optional extras, not standalone products, so they shouldn't compete
   visually with the actual pricing tiers above them. Used for both the
   hosting line under Web pricing and the SMM add-ons list. */
.addon-heading{ font-family:var(--font-display); font-size:20px; font-weight:600; }
.addon-list{
  border-top:1px solid rgba(94, 200, 216, 0.2);
}
.addon-row{
  display:flex; justify-content:space-between; align-items:baseline; gap:24px;
  padding:18px 0; border-bottom:1px solid rgba(255,255,255,0.08);
  flex-wrap:wrap;
}
.addon-row:last-child{ border-bottom:none; }
.addon-text{ flex:1; min-width:240px; }
.addon-name{ font-family:var(--font-display); font-size:16px; font-weight:600; margin-bottom:4px; }
.addon-desc{ font-family:var(--font-body); font-size:13.5px; color:var(--slate); font-style:italic; }
.addon-price{
  font-family:var(--font-mono); font-size:18px; font-weight:700; color:var(--line);
  white-space:nowrap; text-align:right;
}
.addon-unit{ font-size:12px; font-weight:400; color:var(--slate); margin-left:4px; }

@media (max-width:600px){
  .addon-row{ flex-direction:column; gap:8px; }
  .addon-price{ text-align:left; }
}


/* the bundle banner: same liquid-glass recipe as the about-section
   stack-list card, so the two "glass" moments on the site feel like
   the same material */
.pricing-bundle{
  padding:40px; border-radius:16px; text-align:center;
  background:rgba(15, 39, 64, 0.62);
  -webkit-backdrop-filter:blur(16px) saturate(160%);
  backdrop-filter:blur(16px) saturate(160%);
  border:1.5px solid rgba(94, 200, 216, 0.35);
  border-top:1.5px solid rgba(255,255,255,0.26);
  box-shadow:
    0 14px 30px rgba(0,0,0,0.32),
    inset 0 1px 0 rgba(255,255,255,0.12);
}
.bundle-label{
  font-family:var(--font-mono); font-size:12px; letter-spacing:.12em; color:var(--line); margin-bottom:14px;
}
.pricing-bundle h3{ font-family:var(--font-display); font-size:clamp(22px, 3vw, 30px); font-weight:600; margin-bottom:12px; }
.pricing-bundle p{ color:var(--slate); font-size:15px; max-width:560px; margin:0 auto 22px; }
.bundle-discount{ font-family:var(--font-mono); margin-bottom:26px; }
.bundle-discount span:first-child{ font-size:26px; font-weight:700; color:var(--amber); margin-right:8px; }
.bundle-discount span:last-child{ font-size:13px; color:var(--slate); }

@media (max-width:640px){
  .pricing-bundle{ padding:28px 22px; }
  .price-card{ padding:20px; }
  .contact-modal{ padding:16px; }
  .contact-modal-panel{ padding:26px 22px; }
}

/* ---------- FAQ accordion --------------------------------------------
   Native <details>/<summary> — free keyboard support and works even
   before script.js runs (just without the reveal animation). Items are
   rendered by renderFaq() in script.js, from TRANSLATIONS[lang].pricing.faq. */
.faq-list{ display:flex; flex-direction:column; gap:12px; max-width:760px; }
.faq-item{
  border:2px solid var(--grid-line); background:var(--ink-2);
  border-radius:10px; padding:4px 22px; transition:border-color .25s ease;
}
.faq-item[open]{ border-color:var(--line); }
.faq-item summary{
  list-style:none; cursor:pointer; padding:18px 0;
  font-family:var(--font-display); font-size:16px; font-weight:600; color:var(--paper);
  display:flex; align-items:center; justify-content:space-between; gap:16px;
}
.faq-item summary::-webkit-details-marker{ display:none; }
/* plus-becomes-cross indicator, drawn from two CSS bars rather than an
   icon font/SVG so no extra asset is needed for such a small detail */
.faq-item summary{ position:relative; padding-right:26px; }
.faq-item summary::after{
  content:''; flex-shrink:0; width:14px; height:14px;
  position:absolute; right:0; top:50%; transform:translateY(-50%);
  background:
    linear-gradient(var(--line), var(--line)) center / 100% 2px no-repeat,
    linear-gradient(var(--line), var(--line)) center / 2px 100% no-repeat;
  transition:transform .25s ease, opacity .25s ease;
}
.faq-item[open] summary::after{ transform:translateY(-50%) rotate(90deg); opacity:0; }
.faq-item.is-collapsing summary::after{ transform:translateY(-50%) rotate(0deg); opacity:1; }
.faq-item p{ color:var(--slate); font-size:14px; line-height:1.6; padding:0 0 20px; max-width:640px; }


@media (prefers-reduced-motion: reduce){
  html{ scroll-behavior:auto; }
  .sheet{ transition:none; }
  .work-marquee-track{ animation:none; }
  header, nav, .nav-menu, .nav-toggle span{ transition:none; }
  .js .reveal{
    opacity:1; transform:none; filter:none; transition:none;
  }
  .hero-visual-inner{
    animation:none; transition:none;
    transform:rotateX(var(--tiltx)) rotateY(var(--tilty));
  }
  .bb-image::after,
  .stat-card, .cursor-badge,
  .cursor-badge::before{
    animation:none;
  }
  .phone-mockup{
    animation:none;
    transform:rotateY(14deg) rotateX(-4deg);
  }
  .ps-hero::after{
    animation:none;
  }
  .mini-chart polyline{ animation:none; stroke-dashoffset:0; }
  .btn{ transition:background-color .2s, border-color .2s; }
  .process-step, .faq-item{ transition:none; }
}

/* focus visibility */
a:focus-visible, button:focus-visible{ outline:2px solid var(--line); outline-offset:3px; }

/* ---------- skip link ---------------------------------------------------
   First focusable element in the document — lets keyboard/screen-reader
   visitors jump straight past the header/nav into <main>. Sits off-screen
   above the viewport until it receives focus (Tab from a fresh page load),
   then drops into view. #main gets tabindex="-1" in the HTML so it can
   receive programmatic focus when this link is activated; the outline is
   suppressed on #main itself since the skip link already gave a visible
   focus cue. */
.skip-link{
  position:absolute; left:16px; top:-60px;
  background:var(--line); color:var(--ink);
  font-family:var(--font-mono); font-size:13px; font-weight:700;
  padding:12px 20px; border-radius:8px; z-index:200;
  transition:top .2s ease;
}
.skip-link:focus{ top:16px; }
main:focus{ outline:none; }
