/* =============================================================================
   dectrickmcgee.space — one style, fixed.
   =============================================================================
   This replaces the white-label theme system. That system's whole point was
   that a tenant chose colours and element variants at runtime and the page
   rebuilt itself from `/api/config`. It made every surface negotiable, which
   is the opposite of what a personal site wants: one voice, decided once.

   So there are no theme classes here and nothing reads a colour from the API.
   The tokens below are the site. Change them here or not at all.
   ========================================================================== */

:root {
  --bg:        #14110f;
  --bg-lift:   #1c1815;
  --surface:   #221d19;
  --line:      #332b25;
  --line-soft: #272119;
  --text:      #f6efe8;
  --muted:     #a2938a;
  --dim:       #6f645d;
  --accent:    #ff7a45;
  --accent-dim:#c2542a;
  --on-accent: #1a0e08;
  --ok:        #5fd08a;

  --r-sm: 8px;
  --r:    14px;
  --r-lg: 22px;

  --pad: clamp(20px, 5vw, 64px);
  --maxw: 1180px;

  --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Inter, Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* A single warm wash behind everything. Fixed, so it does not travel with the
   scroll and turn into a gradient band halfway down a long page. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(900px 500px at 12% -8%, rgba(255,122,69,.16), transparent 62%),
    radial-gradient(700px 480px at 92% 4%, rgba(255,122,69,.07), transparent 60%);
  z-index: 0;
}

.wrap { position: relative; z-index: 1; max-width: var(--maxw); margin: 0 auto; padding: 0 var(--pad); }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

h1, h2, h3 { line-height: 1.15; letter-spacing: -0.02em; margin: 0; font-weight: 650; }
h1 { font-size: clamp(2.1rem, 5.4vw, 3.6rem); letter-spacing: -0.035em; }
h2 { font-size: clamp(1.5rem, 3vw, 2.1rem); }
h3 { font-size: 1.06rem; }
p  { margin: 0 0 1rem; }

.eyebrow {
  font-family: var(--mono);
  font-size: .72rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--dim);
  margin: 0 0 .8rem;
}

/* ---------- header ------------------------------------------------------- */

.site-head {
  position: sticky; top: 0; z-index: 50;
  backdrop-filter: blur(14px);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  border-bottom: 1px solid var(--line-soft);
}
.site-head__in { max-width: var(--maxw); margin: 0 auto; padding: 14px var(--pad);
                 display: flex; align-items: center; gap: 22px; }
.brand { display: flex; align-items: center; gap: 11px; color: var(--text); font-weight: 650; }
.brand:hover { text-decoration: none; }
.brand__mark {
  width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center;
  background: linear-gradient(140deg, var(--accent), var(--accent-dim));
  color: var(--on-accent); font-weight: 800; font-size: .84rem; letter-spacing: -.02em;
}
.site-nav { margin-left: auto; display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.site-nav a {
  color: var(--muted); font-size: .9rem; padding: 7px 12px; border-radius: 999px;
}
.site-nav a:hover { color: var(--text); background: var(--bg-lift); text-decoration: none; }
.site-nav a.cta {
  background: var(--accent); color: var(--on-accent); font-weight: 640;
}
.site-nav a.cta:hover { background: #ff8d5e; }

/* ---------- hero --------------------------------------------------------- */

/* Vertical only. `padding:` shorthand here would reset the horizontal gutter
   that .wrap supplies on the same element, putting text on the screen edge. */
.hero { padding-top: clamp(48px, 9vw, 104px); padding-bottom: clamp(32px, 5vw, 56px); }
.hero__grid { display: grid; gap: clamp(28px, 5vw, 56px); grid-template-columns: 1fr; align-items: center; }
@media (min-width: 900px) { .hero__grid { grid-template-columns: 1.35fr .85fr; } }

.status {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: .82rem; color: var(--muted);
  border: 1px solid var(--line); border-radius: 999px; padding: 6px 14px;
  margin-bottom: 20px; background: var(--bg-lift);
}
.status i { width: 7px; height: 7px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 4px rgba(95,208,138,.14); }

.hero p.lede { color: var(--muted); font-size: 1.06rem; max-width: 60ch; margin-top: 20px; }
.hero__cta { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 28px; }

.btn {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  padding: 11px 20px; border-radius: 999px; font-size: .93rem; font-weight: 600;
  border: 1px solid transparent; font-family: inherit;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.btn--primary { background: var(--accent); color: var(--on-accent); }
.btn--primary:hover { background: #ff8d5e; text-decoration: none; }
.btn--ghost { border-color: var(--line); color: var(--text); background: transparent; }
.btn--ghost:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.btn--sm { padding: 8px 15px; font-size: .85rem; }

.portrait { margin: 0; }
.portrait img {
  width: 100%; height: auto; display: block; border-radius: var(--r-lg);
  border: 1px solid var(--line); background: var(--surface);
}

/* ---------- sections ----------------------------------------------------- */

section.band {
  /* Vertical only — see .hero above. `section.band` also out-specifies .wrap,
     so the shorthand won and every section lost its gutter. */
  padding-top: clamp(48px, 7vw, 88px); padding-bottom: clamp(48px, 7vw, 88px);
  border-top: 1px solid var(--line-soft);
}
.section-head { max-width: 62ch; margin-bottom: clamp(26px, 4vw, 42px); }
.section-head p { color: var(--muted); margin: 12px 0 0; }

/* ---------- the work grid ------------------------------------------------ */

.work { display: grid; gap: 18px; grid-template-columns: 1fr; }
@media (min-width: 700px)  { .work { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1040px) { .work { grid-template-columns: repeat(3, 1fr); } }

.card {
  border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface);
  padding: 22px; display: flex; flex-direction: column; gap: 12px;
  transition: border-color .16s ease, transform .16s ease;
}
.card:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); transform: translateY(-2px); }
.card__top { display: flex; align-items: center; gap: 11px; }
.card__dot { width: 9px; height: 9px; border-radius: 50%; background: var(--accent); flex: none; }
.card__dot[data-live="no"] { background: var(--dim); }
.card h3 { flex: 1; }
.card__url {
  font-family: var(--mono); font-size: .76rem; color: var(--dim);
  overflow-wrap: anywhere;
}
.card p { color: var(--muted); font-size: .93rem; margin: 0; flex: 1; }
.tags { display: flex; flex-wrap: wrap; gap: 6px; }
.tag {
  font-family: var(--mono); font-size: .68rem; color: var(--muted);
  border: 1px solid var(--line); border-radius: 999px; padding: 3px 9px;
}
.card__actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 4px; }

/* ---------- the viewer: an app framed in place --------------------------- */

.viewer { position: fixed; inset: 0; z-index: 100; display: none; }
.viewer[open] { display: block; }
.viewer__scrim { position: absolute; inset: 0; background: rgba(8,6,5,.78); backdrop-filter: blur(6px); }
.viewer__panel {
  position: absolute; inset: clamp(10px, 2.4vw, 34px);
  display: flex; flex-direction: column;
  background: var(--bg-lift); border: 1px solid var(--line);
  border-radius: var(--r-lg); overflow: hidden;
  box-shadow: 0 30px 90px rgba(0,0,0,.6);
}
.viewer__bar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 11px 14px; border-bottom: 1px solid var(--line);
  background: var(--surface); flex: none;
}
.viewer__lights { display: flex; gap: 6px; }
.viewer__lights i { width: 11px; height: 11px; border-radius: 50%; background: var(--line); display: block; }
.viewer__addr {
  flex: 1; min-width: 180px;
  font-family: var(--mono); font-size: .78rem; color: var(--muted);
  background: var(--bg); border: 1px solid var(--line); border-radius: 999px;
  padding: 6px 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.viewer__body { flex: 1; position: relative; background: #fff; }
.viewer__body iframe { width: 100%; height: 100%; border: 0; display: block; }

/* Shown instead of the frame when a site refuses to be embedded. Honest by
   design: the alternative is a blank white rectangle the visitor reads as
   "his site is broken", when it is the other site declining. */
.noframe {
  position: absolute; inset: 0; display: grid; place-items: center; text-align: center;
  padding: 32px; background: var(--bg-lift); color: var(--muted);
}
.noframe__in { max-width: 46ch; }
.noframe h4 { color: var(--text); margin: 0 0 10px; font-size: 1.1rem; }
.noframe code { font-family: var(--mono); font-size: .8rem; color: var(--accent); }

/* ---------- chat --------------------------------------------------------- */

.chat-shell {
  border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface);
  display: flex; flex-direction: column; overflow: hidden;
  height: min(620px, 74vh);
  height: min(620px, 74dvh);   /* dvh tracks the mobile address bar; vh above is the fallback */
  -webkit-overflow-scrolling: touch;
}
.chat-log { flex: 1; overflow-y: auto; padding: 22px; display: flex; flex-direction: column; gap: 14px; }
.msg { max-width: 78%; padding: 12px 16px; border-radius: 16px; font-size: .95rem; line-height: 1.55; }
.msg--me { align-self: flex-end; background: var(--accent); color: var(--on-accent); border-bottom-right-radius: 5px; }
.msg--ai { align-self: flex-start; background: var(--bg-lift); border: 1px solid var(--line); border-bottom-left-radius: 5px; }
.composer { display: flex; gap: 10px; padding: 14px; border-top: 1px solid var(--line); background: var(--bg-lift); }
.composer input {
  flex: 1; background: var(--bg); border: 1px solid var(--line); border-radius: 999px;
  padding: 12px 18px; color: var(--text); font: inherit; font-size: .95rem;
}
.composer input:focus { outline: none; border-color: var(--accent); }

/* ---------- lists -------------------------------------------------------- */

.cols { display: grid; gap: 18px; grid-template-columns: 1fr; }
@media (min-width: 760px) { .cols { grid-template-columns: repeat(2, 1fr); } }

.stack-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; }
.stack-list li { border-top: 1px solid var(--line-soft); padding-top: 14px; }
.stack-list b { display: block; font-size: .82rem; color: var(--dim);
                font-family: var(--mono); text-transform: uppercase; letter-spacing: .1em; margin-bottom: 6px; }
.stack-list span { color: var(--muted); font-size: .93rem; }

/* ---------- footer ------------------------------------------------------- */

.site-foot { border-top: 1px solid var(--line-soft); padding: 44px 0 60px; color: var(--dim); font-size: .88rem; }
.site-foot a { color: var(--muted); }
.site-foot__in { display: flex; gap: 18px; flex-wrap: wrap; align-items: center; }
.site-foot__in .sp { margin-left: auto; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }


/* =============================================================================
   Phones and tablets
   =============================================================================
   Written after reading the desktop rules back, not from a template. Each block
   below is a specific failure, not a precaution.
   ========================================================================== */

/* The header put a wordmark and four links in one flex row. At 360px that is
   wider than the screen, and `flex-wrap` on the nav alone just pushed the links
   under the brand at ragged widths. Under 620px the wordmark goes and the nav
   becomes the row. */
@media (max-width: 620px) {
  .site-head__in { gap: 12px; padding: 11px var(--pad); }
  .brand span:not(.brand__mark) { display: none; }
  .site-nav { margin-left: auto; gap: 2px; }
  .site-nav a { padding: 8px 10px; font-size: .86rem; }
}

/* Below ~430px even that is tight, so drop the two section links and keep the
   two that matter: the work and the way to hire me. */
@media (max-width: 430px) {
  .site-nav a[href="#capability"], .site-nav a[href="#chat"] { display: none; }
}

/* Long words — a URL, a product name — overflowing their box is the most common
   way a page gains a horizontal scrollbar on a phone. */
h1, h2, h3, p, .card__url, .viewer__addr { overflow-wrap: break-word; }
h1 { hyphens: auto; }

/* The viewer is the point of the page, so on a phone it takes the whole screen
   rather than floating inset with 10px of scrim nobody can tap accurately. */
@media (max-width: 620px) {
  .viewer__panel { inset: 0; border-radius: 0; border: 0; }
  .viewer__bar { padding: 9px 10px; gap: 8px; }
  .viewer__lights { display: none; }          /* decoration, and it costs 40px */
  .viewer__addr { min-width: 0; flex: 1 1 100%; order: 3; font-size: .72rem; }
  #viewer-open, .viewer__bar button { flex: 1; justify-content: center; }
}

/* iOS will not scroll an iframe inside a fixed ancestor unless the frame is
   given its own scrolling wrapper. Without this the embedded sites open at the
   top and stay there, which reads as "the preview is broken". */
@media (hover: none) {
  .viewer__body { -webkit-overflow-scrolling: touch; overflow: auto; }
  .viewer__body iframe { min-height: 100%; }
}

/* 34px is under every touch-target guideline. Only raised where the pointer is
   actually coarse, so the desktop density is unchanged. */
@media (pointer: coarse) {
  .btn { padding: 13px 22px; }
  .btn--sm { padding: 11px 16px; font-size: .88rem; }
  .site-nav a { padding: 10px 12px; }
}

/* The footer's `margin-left:auto` spacer does nothing once the row wraps, and
   left a lonely line adrift under the email address. */
@media (max-width: 620px) {
  .site-foot__in { flex-direction: column; align-items: flex-start; gap: 8px; }
  .site-foot__in .sp { display: none; }
  .site-foot { padding: 32px 0 44px; }
}

/* Chat bubbles at 78% of a 360px screen are 280px, which wraps mid-sentence
   constantly. Give them the room. */
@media (max-width: 620px) {
  .msg { max-width: 92%; }
  .chat-log { padding: 16px; gap: 11px; }
  .composer { padding: 11px; }
}



/* The chat used to live on this page and its styles lived here. Both moved to
   chat.dectrickmcgee.space on 2026-09-05: static/css/chat.css for the assistant
   and static/css/workbench.css for the previewer. The portfolio should not
   download rules for surfaces it no longer has. */